package tlsfingerprint

import (
	"testing"

	"github.com/Wei-Shaw/sub2api/internal/config"
)

func TestNewRegistry(t *testing.T) {
	r := NewRegistry()

	// Should have exactly one profile (the default)
	if r.ProfileCount() != 1 {
		t.Errorf("expected 1 profile, got %d", r.ProfileCount())
	}

	// Should have the default profile
	profile := r.GetDefaultProfile()
	if profile == nil {
		t.Error("expected default profile to exist")
	}

	// Default profile name should be in the list
	names := r.ProfileNames()
	if len(names) != 1 || names[0] != DefaultProfileName {
		t.Errorf("expected profile names to be [%s], got %v", DefaultProfileName, names)
	}
}

func TestRegisterProfile(t *testing.T) {
	r := NewRegistry()

	// Register a new profile
	customProfile := &Profile{
		Name:         "Custom Profile",
		EnableGREASE: true,
	}
	r.RegisterProfile("custom", customProfile)

	// Should now have 2 profiles
	if r.ProfileCount() != 2 {
		t.Errorf("expected 2 profiles, got %d", r.ProfileCount())
	}

	// Should be able to retrieve the custom profile
	retrieved := r.GetProfile("custom")
	if retrieved == nil {
		t.Fatal("expected custom profile to exist")
	}
	if retrieved.Name != "Custom Profile" {
		t.Errorf("expected profile name 'Custom Profile', got '%s'", retrieved.Name)
	}
	if !retrieved.EnableGREASE {
		t.Error("expected EnableGREASE to be true")
	}
}

func TestGetProfile(t *testing.T) {
	r := NewRegistry()

	// Get existing profile
	profile := r.GetProfile(DefaultProfileName)
	if profile == nil {
		t.Error("expected default profile to exist")
	}

	// Get non-existing profile
	nonExistent := r.GetProfile("nonexistent")
	if nonExistent != nil {
		t.Error("expected nil for non-existent profile")
	}
}

func TestGetProfileByAccountID(t *testing.T) {
	r := NewRegistry()

	// With only default profile, all account IDs should return the same profile
	for i := int64(0); i < 10; i++ {
		profile := r.GetProfileByAccountID(i)
		if profile == nil {
			t.Errorf("expected profile for account %d, got nil", i)
		}
	}

	// Add more profiles
	r.RegisterProfile("profile_a", &Profile{Name: "Profile A"})
	r.RegisterProfile("profile_b", &Profile{Name: "Profile B"})

	// Now we have 3 profiles: claude_cli_v2, profile_a, profile_b
	// Names are sorted, so order is: claude_cli_v2, profile_a, profile_b
	expectedOrder := []string{DefaultProfileName, "profile_a", "profile_b"}
	names := r.ProfileNames()
	for i, name := range expectedOrder {
		if names[i] != name {
			t.Errorf("expected name at index %d to be %s, got %s", i, name, names[i])
		}
	}

	// Test modulo selection
	// Account ID 0 % 3 = 0 -> claude_cli_v2
	// Account ID 1 % 3 = 1 -> profile_a
	// Account ID 2 % 3 = 2 -> profile_b
	// Account ID 3 % 3 = 0 -> claude_cli_v2
	testCases := []struct {
		accountID    int64
		expectedName string
	}{
		{0, "Claude CLI 2.x (Node.js 20.x + OpenSSL 3.x)"},
		{1, "Profile A"},
		{2, "Profile B"},
		{3, "Claude CLI 2.x (Node.js 20.x + OpenSSL 3.x)"},
		{4, "Profile A"},
		{5, "Profile B"},
		{100, "Profile A"}, // 100 % 3 = 1
		{-1, "Profile A"},  // |-1| % 3 = 1
		{-3, "Claude CLI 2.x (Node.js 20.x + OpenSSL 3.x)"}, // |-3| % 3 = 0
	}

	for _, tc := range testCases {
		profile := r.GetProfileByAccountID(tc.accountID)
		if profile == nil {
			t.Errorf("expected profile for account %d, got nil", tc.accountID)
			continue
		}
		if profile.Name != tc.expectedName {
			t.Errorf("account %d: expected profile name '%s', got '%s'", tc.accountID, tc.expectedName, profile.Name)
		}
	}
}

func TestNewRegistryFromConfig(t *testing.T) {
	// Test with nil config
	r := NewRegistryFromConfig(nil)
	if r.ProfileCount() != 1 {
		t.Errorf("expected 1 profile with nil config, got %d", r.ProfileCount())
	}

	// Test with disabled config
	disabledCfg := &config.TLSFingerprintConfig{
		Enabled: false,
	}
	r = NewRegistryFromConfig(disabledCfg)
	if r.ProfileCount() != 1 {
		t.Errorf("expected 1 profile with disabled config, got %d", r.ProfileCount())
	}

	// Test with enabled config and custom profiles
	enabledCfg := &config.TLSFingerprintConfig{
		Enabled: true,
		Profiles: map[string]config.TLSProfileConfig{
			"custom1": {
				Name:         "Custom Profile 1",
				EnableGREASE: true,
			},
			"custom2": {
				Name:         "Custom Profile 2",
				EnableGREASE: false,
			},
		},
	}
	r = NewRegistryFromConfig(enabledCfg)

	// Should have 3 profiles: default + 2 custom
	if r.ProfileCount() != 3 {
		t.Errorf("expected 3 profiles, got %d", r.ProfileCount())
	}

	// Check custom profiles exist
	custom1 := r.GetProfile("custom1")
	if custom1 == nil || custom1.Name != "Custom Profile 1" {
		t.Error("expected custom1 profile to exist with correct name")
	}
	custom2 := r.GetProfile("custom2")
	if custom2 == nil || custom2.Name != "Custom Profile 2" {
		t.Error("expected custom2 profile to exist with correct name")
	}
}

func TestProfileNames(t *testing.T) {
	r := NewRegistry()

	// Add profiles in non-alphabetical order
	r.RegisterProfile("zebra", &Profile{Name: "Zebra"})
	r.RegisterProfile("alpha", &Profile{Name: "Alpha"})
	r.RegisterProfile("beta", &Profile{Name: "Beta"})

	names := r.ProfileNames()

	// Should be sorted alphabetically
	expected := []string{"alpha", "beta", DefaultProfileName, "zebra"}
	if len(names) != len(expected) {
		t.Errorf("expected %d names, got %d", len(expected), len(names))
	}
	for i, name := range expected {
		if names[i] != name {
			t.Errorf("expected name at index %d to be %s, got %s", i, name, names[i])
		}
	}

	// Test that returned slice is a copy (modifying it shouldn't affect registry)
	names[0] = "modified"
	originalNames := r.ProfileNames()
	if originalNames[0] == "modified" {
		t.Error("modifying returned slice should not affect registry")
	}
}

func TestConcurrentAccess(t *testing.T) {
	r := NewRegistry()

	// Run concurrent reads and writes
	done := make(chan bool)

	// Writers
	for i := 0; i < 10; i++ {
		go func(id int) {
			for j := 0; j < 100; j++ {
				r.RegisterProfile("concurrent"+string(rune('0'+id)), &Profile{Name: "Concurrent"})
			}
			done <- true
		}(i)
	}

	// Readers
	for i := 0; i < 10; i++ {
		go func(id int) {
			for j := 0; j < 100; j++ {
				_ = r.ProfileCount()
				_ = r.ProfileNames()
				_ = r.GetProfileByAccountID(int64(id * j))
				_ = r.GetProfile(DefaultProfileName)
			}
			done <- true
		}(i)
	}

	// Wait for all goroutines
	for i := 0; i < 20; i++ {
		<-done
	}

	// Test should pass without data races (run with -race flag)
}
