package cart

import (
	"encoding/json"
	"fmt"
	"testing"
)

// TestNewCartIdUniqueness generates many ids and checks for collisions.
func TestNewCartIdUniqueness(t *testing.T) {
	const n = 20000
	seen := make(map[string]struct{}, n)
	for i := 0; i < n; i++ {
		id, err := NewCartId()
		if err != nil {
			t.Fatalf("NewCartId error: %v", err)
		}
		s := id.String()
		if _, exists := seen[s]; exists {
			t.Fatalf("duplicate id encountered: %s", s)
		}
		seen[s] = struct{}{}
		if s == "" {
			t.Fatalf("empty string representation for id %d", id)
		}
		if len(s) > 11 {
			t.Fatalf("encoded id length exceeds 11 chars: %s (%d)", s, len(s))
		}
		if id == 0 {
			// We force regeneration on zero, extremely unlikely but test guards intent.
			t.Fatalf("zero id generated (should be regenerated)")
		}
	}
}

// TestParseCartIdRoundTrip ensures parse -> string -> parse is stable.
func TestParseCartIdRoundTrip(t *testing.T) {
	id := MustNewCartId()
	txt := id.String()
	parsed, ok := ParseCartId(txt)
	if !ok {
		t.Fatalf("ParseCartId failed for valid text %q", txt)
	}
	if parsed != id {
		t.Fatalf("round trip mismatch: original=%d parsed=%d txt=%s", id, parsed, txt)
	}
}

// TestParseCartIdInvalid covers invalid inputs.
func TestParseCartIdInvalid(t *testing.T) {
	invalid := []string{
		"",               // empty
		" ",              // space
		"01234567890abc", // >11 chars
		"!!!!",           // invalid chars
		"-underscore-",   // invalid chars
		"abc_def",        // underscore invalid for base62
		"0123456789ABCD", // 14 chars
	}
	for _, s := range invalid {
		if _, ok := ParseCartId(s); ok {
			t.Fatalf("expected parse failure for %q", s)
		}
	}
}

// TestMustParseCartIdPanics verifies panic behavior for invalid input.
func TestMustParseCartIdPanics(t *testing.T) {
	defer func() {
		if r := recover(); r == nil {
			t.Fatalf("expected panic for invalid MustParseCartId input")
		}
	}()
	_ = MustParseCartId("not*base62")
}

// TestJSONMarshalUnmarshalCartId verifies JSON round trip.
func TestJSONMarshalUnmarshalCartId(t *testing.T) {
	id := MustNewCartId()
	data, err := json.Marshal(struct {
		Cart CartId `json:"cart"`
	}{Cart: id})
	if err != nil {
		t.Fatalf("marshal error: %v", err)
	}
	var out struct {
		Cart CartId `json:"cart"`
	}
	if err := json.Unmarshal(data, &out); err != nil {
		t.Fatalf("unmarshal error: %v", err)
	}
	if out.Cart != id {
		t.Fatalf("JSON round trip mismatch: have %d got %d", id, out.Cart)
	}
}

// TestBase62LengthBound checks worst-case length (near max uint64).
func TestBase62LengthBound(t *testing.T) {
	// Largest uint64
	const maxU64 = ^uint64(0)
	s := encodeBase62(maxU64)
	if len(s) > 11 {
		t.Fatalf("max uint64 encoded length > 11: %d (%s)", len(s), s)
	}
	dec, ok := decodeBase62(s)
	if !ok || dec != maxU64 {
		t.Fatalf("decode failed for max uint64: ok=%v dec=%d want=%d", ok, dec, maxU64)
	}
}

// TestZeroEncoding ensures zero value encodes to "0" and parses back.
func TestZeroEncoding(t *testing.T) {
	if s := encodeBase62(0); s != "0" {
		t.Fatalf("encodeBase62(0) expected '0', got %q", s)
	}
	v, ok := decodeBase62("0")
	if !ok || v != 0 {
		t.Fatalf("decodeBase62('0') failed: ok=%v v=%d", ok, v)
	}
	if _, ok := ParseCartId("0"); !ok {
		t.Fatalf("ParseCartId(\"0\") should succeed")
	}
}

// TestSequentialParse ensures sequentially generated ids parse correctly.
func TestSequentialParse(t *testing.T) {
	for i := 0; i < 1000; i++ {
		id := MustNewCartId()
		txt := id.String()
		parsed, ok := ParseCartId(txt)
		if !ok || parsed != id {
			t.Fatalf("sequential parse mismatch: idx=%d orig=%d parsed=%d txt=%s", i, id, parsed, txt)
		}
	}
}

// BenchmarkNewCartId measures generation performance.
func BenchmarkNewCartId(b *testing.B) {
	for i := 0; i < b.N; i++ {
		if _, err := NewCartId(); err != nil {
			b.Fatalf("NewCartId error: %v", err)
		}
	}
}

// BenchmarkEncodeBase62 measures encoding performance.
func BenchmarkEncodeBase62(b *testing.B) {
	// Precompute sample values
	samples := make([]uint64, 1024)
	for i := range samples {
		// Spread bits without crypto randomness overhead
		samples[i] = (uint64(i) << 53) ^ (uint64(i) * 0x9E3779B185EBCA87)
	}
	b.ResetTimer()
	var sink string
	for i := 0; i < b.N; i++ {
		sink = encodeBase62(samples[i%len(samples)])
	}
	_ = sink
}

// BenchmarkDecodeBase62 measures decoding performance.
func BenchmarkDecodeBase62(b *testing.B) {
	encoded := make([]string, 1024)
	for i := range encoded {
		encoded[i] = encodeBase62((uint64(i) << 32) | uint64(i))
	}
	b.ResetTimer()
	var sum uint64
	for i := 0; i < b.N; i++ {
		v, ok := decodeBase62(encoded[i%len(encoded)])
		if !ok {
			b.Fatalf("decode failure for %s", encoded[i%len(encoded)])
		}
		sum ^= v
	}
	_ = sum
}

// ExampleCartIdString documents usage of CartId string form.
func ExampleCartId_string() {
	id := MustNewCartId()
	fmt.Println(len(id.String()) <= 11) // outputs true
	// Output: true
}
