package token

import (
	"testing"
	"time"
)

func TestGenerate_Length(t *testing.T) {
	tests := []struct {
		name   string
		length int
	}{
		{"zero length", 0},
		{"short", 5},
		{"medium", 32},
		{"long", 100},
		{"very long", 1000},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := Generate(tt.length)
			if len(result) != tt.length {
				t.Errorf("Generate(%d) length = %d, want %d", tt.length, len(result), tt.length)
			}
		})
	}
}

func TestGenerate_CharacterSet(t *testing.T) {
	// Verify that generated tokens only contain alphanumeric characters
	validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	validCharMap := make(map[rune]bool)
	for _, c := range validChars {
		validCharMap[c] = true
	}

	token := Generate(1000) // Large sample
	for _, c := range token {
		if !validCharMap[c] {
			t.Errorf("Generate() produced invalid character: %c", c)
		}
	}
}

func TestGenerate_Randomness(t *testing.T) {
	// Generate multiple tokens and verify they're different
	tokens := make(map[string]bool)
	count := 100
	length := 32

	for i := 0; i < count; i++ {
		token := Generate(length)
		if tokens[token] {
			t.Errorf("Generate() produced duplicate token: %s", token)
		}
		tokens[token] = true
	}

	if len(tokens) != count {
		t.Errorf("Generated %d unique tokens, want %d", len(tokens), count)
	}
}

func TestGenerate_ContainsUppercase(t *testing.T) {
	// With enough characters, should contain at least one uppercase letter
	token := Generate(1000)
	hasUpper := false
	for _, c := range token {
		if c >= 'A' && c <= 'Z' {
			hasUpper = true
			break
		}
	}
	if !hasUpper {
		t.Error("Generate(1000) should contain at least one uppercase letter")
	}
}

func TestGenerate_ContainsLowercase(t *testing.T) {
	// With enough characters, should contain at least one lowercase letter
	token := Generate(1000)
	hasLower := false
	for _, c := range token {
		if c >= 'a' && c <= 'z' {
			hasLower = true
			break
		}
	}
	if !hasLower {
		t.Error("Generate(1000) should contain at least one lowercase letter")
	}
}

func TestGenerate_ContainsDigit(t *testing.T) {
	// With enough characters, should contain at least one digit
	token := Generate(1000)
	hasDigit := false
	for _, c := range token {
		if c >= '0' && c <= '9' {
			hasDigit = true
			break
		}
	}
	if !hasDigit {
		t.Error("Generate(1000) should contain at least one digit")
	}
}

func TestGenerate_Distribution(t *testing.T) {
	// Test that characters are reasonably distributed
	token := Generate(6200) // 62 chars * 100 = good sample size
	charCount := make(map[rune]int)

	for _, c := range token {
		charCount[c]++
	}

	// With 62 valid characters and 6200 samples, average should be 100 per char
	// We'll accept a range to account for randomness
	minExpected := 50 // Allow some variance
	maxExpected := 150

	for c, count := range charCount {
		if count < minExpected || count > maxExpected {
			t.Logf("Character %c appeared %d times (outside expected range %d-%d)", c, count, minExpected, maxExpected)
		}
	}

	// Just verify we have a good spread of characters
	if len(charCount) < 50 {
		t.Errorf("Only %d different characters used, want at least 50", len(charCount))
	}
}

func TestNewSafeRand(t *testing.T) {
	rng := NewSafeRand()
	if rng == nil {
		t.Fatal("NewSafeRand() returned nil")
	}

	// Test that it produces different values on subsequent calls
	val1 := rng.Intn(1000000)
	val2 := rng.Intn(1000000)

	if val1 == val2 {
		// This is possible but unlikely, let's try a few more times
		same := true
		for i := 0; i < 10; i++ {
			if rng.Intn(1000000) != val1 {
				same = false
				break
			}
		}
		if same {
			t.Error("NewSafeRand() produced same value 12 times in a row")
		}
	}
}

func TestRNG_GlobalVariable(t *testing.T) {
	// Test that the global RNG variable is initialized
	if RNG == nil {
		t.Fatal("Global RNG is nil")
	}

	// Test that it works
	val := RNG.Intn(100)
	if val < 0 || val >= 100 {
		t.Errorf("RNG.Intn(100) = %d, out of range [0, 100)", val)
	}
}

func TestRNG_Uint32(t *testing.T) {
	// Test that RNG can generate uint32 values
	val1 := RNG.Uint32()
	val2 := RNG.Uint32()

	// They should be different (with very high probability)
	if val1 == val2 {
		// Try a few more times
		same := true
		for i := 0; i < 10; i++ {
			if RNG.Uint32() != val1 {
				same = false
				break
			}
		}
		if same {
			t.Error("RNG.Uint32() produced same value 12 times")
		}
	}
}

func TestGenerate_Concurrency(t *testing.T) {
	// Test that Generate works correctly when called concurrently
	done := make(chan string, 100)

	for i := 0; i < 100; i++ {
		go func() {
			token := Generate(32)
			done <- token
		}()
	}

	tokens := make(map[string]bool)
	for i := 0; i < 100; i++ {
		token := <-done
		if len(token) != 32 {
			t.Errorf("Token length = %d, want 32", len(token))
		}
		tokens[token] = true
	}

	// Should have many unique tokens (allow some small chance of duplicates)
	if len(tokens) < 95 {
		t.Errorf("Only %d unique tokens from 100 concurrent calls", len(tokens))
	}
}

func TestGenerate_EmptyString(t *testing.T) {
	token := Generate(0)
	if token != "" {
		t.Errorf("Generate(0) = %q, want empty string", token)
	}
}

func TestGenerate_OnlyAlphanumeric(t *testing.T) {
	// Verify no special characters
	token := Generate(1000)
	for i, c := range token {
		isValid := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
		if !isValid {
			t.Errorf("Token[%d] = %c (invalid character)", i, c)
		}
	}
}

func TestNewSafeRand_DifferentSeeds(t *testing.T) {
	// Create two RNGs at different times and verify they produce different sequences
	rng1 := NewSafeRand()
	time.Sleep(1 * time.Millisecond) // Ensure different seed
	rng2 := NewSafeRand()

	val1 := rng1.Intn(1000000)
	val2 := rng2.Intn(1000000)

	// They should be different with high probability
	if val1 == val2 {
		// Try again
		val1 = rng1.Intn(1000000)
		val2 = rng2.Intn(1000000)
		if val1 == val2 {
			t.Log("Two RNGs created at different times produced same first two values (possible but unlikely)")
		}
	}
}

func BenchmarkGenerate_Short(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = Generate(8)
	}
}

func BenchmarkGenerate_Medium(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = Generate(32)
	}
}

func BenchmarkGenerate_Long(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = Generate(128)
	}
}

func BenchmarkNewSafeRand(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = NewSafeRand()
	}
}

func BenchmarkRNG_Intn(b *testing.B) {
	rng := NewSafeRand()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = rng.Intn(62)
	}
}

func BenchmarkRNG_Uint32(b *testing.B) {
	rng := NewSafeRand()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = rng.Uint32()
	}
}

func TestGenerate_ConsistentCharacterSet(t *testing.T) {
	// Verify the character set matches what's defined in the code
	expectedChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	if len(expectedChars) != 62 {
		t.Errorf("Expected character set length = %d, want 62", len(expectedChars))
	}

	// Count each type
	lowercase := 0
	uppercase := 0
	digits := 0
	for _, c := range expectedChars {
		if c >= 'a' && c <= 'z' {
			lowercase++
		} else if c >= 'A' && c <= 'Z' {
			uppercase++
		} else if c >= '0' && c <= '9' {
			digits++
		}
	}

	if lowercase != 26 {
		t.Errorf("Lowercase count = %d, want 26", lowercase)
	}
	if uppercase != 26 {
		t.Errorf("Uppercase count = %d, want 26", uppercase)
	}
	if digits != 10 {
		t.Errorf("Digits count = %d, want 10", digits)
	}
}

func TestRNG_Type(t *testing.T) {
	// Verify RNG is of type *SafeRand
	var _ = (*SafeRand)(nil)
	_ = RNG
	_ = NewSafeRand()
}
