package synthesizer

import (
	"reflect"
	"strings"
	"testing"

	"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
	coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
)

func TestNewStableIDGenerator(t *testing.T) {
	gen := NewStableIDGenerator()
	if gen == nil {
		t.Fatal("expected non-nil generator")
	}
	if gen.counters == nil {
		t.Fatal("expected non-nil counters map")
	}
}

func TestStableIDGenerator_Next(t *testing.T) {
	tests := []struct {
		name       string
		kind       string
		parts      []string
		wantPrefix string
	}{
		{
			name:       "basic gemini apikey",
			kind:       "gemini:apikey",
			parts:      []string{"test-key", ""},
			wantPrefix: "gemini:apikey:",
		},
		{
			name:       "claude with base url",
			kind:       "claude:apikey",
			parts:      []string{"sk-ant-xxx", "https://api.anthropic.com"},
			wantPrefix: "claude:apikey:",
		},
		{
			name:       "empty parts",
			kind:       "codex:apikey",
			parts:      []string{},
			wantPrefix: "codex:apikey:",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gen := NewStableIDGenerator()
			id, short := gen.Next(tt.kind, tt.parts...)

			if !strings.Contains(id, tt.wantPrefix) {
				t.Errorf("expected id to contain %q, got %q", tt.wantPrefix, id)
			}
			if short == "" {
				t.Error("expected non-empty short id")
			}
			if len(short) != 12 {
				t.Errorf("expected short id length 12, got %d", len(short))
			}
		})
	}
}

func TestStableIDGenerator_Stability(t *testing.T) {
	gen1 := NewStableIDGenerator()
	gen2 := NewStableIDGenerator()

	id1, _ := gen1.Next("gemini:apikey", "test-key", "https://api.example.com")
	id2, _ := gen2.Next("gemini:apikey", "test-key", "https://api.example.com")

	if id1 != id2 {
		t.Errorf("same inputs should produce same ID: got %q and %q", id1, id2)
	}
}

func TestStableIDGenerator_CollisionHandling(t *testing.T) {
	gen := NewStableIDGenerator()

	id1, short1 := gen.Next("gemini:apikey", "same-key")
	id2, short2 := gen.Next("gemini:apikey", "same-key")

	if id1 == id2 {
		t.Error("collision should be handled with suffix")
	}
	if short1 == short2 {
		t.Error("short ids should differ")
	}
	if !strings.Contains(short2, "-1") {
		t.Errorf("second short id should contain -1 suffix, got %q", short2)
	}
}

func TestStableIDGenerator_NilReceiver(t *testing.T) {
	var gen *StableIDGenerator = nil
	id, short := gen.Next("test:kind", "part")

	if id != "test:kind:000000000000" {
		t.Errorf("expected test:kind:000000000000, got %q", id)
	}
	if short != "000000000000" {
		t.Errorf("expected 000000000000, got %q", short)
	}
}

func TestApplyAuthExcludedModelsMeta(t *testing.T) {
	tests := []struct {
		name     string
		auth     *coreauth.Auth
		cfg      *config.Config
		perKey   []string
		authKind string
		wantHash bool
		wantKind string
	}{
		{
			name: "apikey with excluded models",
			auth: &coreauth.Auth{
				Provider:   "gemini",
				Attributes: make(map[string]string),
			},
			cfg:      &config.Config{},
			perKey:   []string{"model-a", "model-b"},
			authKind: "apikey",
			wantHash: true,
			wantKind: "apikey",
		},
		{
			name: "oauth with provider excluded models",
			auth: &coreauth.Auth{
				Provider:   "claude",
				Attributes: make(map[string]string),
			},
			cfg: &config.Config{
				OAuthExcludedModels: map[string][]string{
					"claude": {"claude-2.0"},
				},
			},
			perKey:   nil,
			authKind: "oauth",
			wantHash: true,
			wantKind: "oauth",
		},
		{
			name: "nil auth",
			auth: nil,
			cfg:  &config.Config{},
		},
		{
			name:     "nil config",
			auth:     &coreauth.Auth{Provider: "test"},
			cfg:      nil,
			authKind: "apikey",
		},
		{
			name: "nil attributes initialized",
			auth: &coreauth.Auth{
				Provider:   "gemini",
				Attributes: nil,
			},
			cfg:      &config.Config{},
			perKey:   []string{"model-x"},
			authKind: "apikey",
			wantHash: true,
			wantKind: "apikey",
		},
		{
			name: "apikey with duplicate excluded models",
			auth: &coreauth.Auth{
				Provider:   "gemini",
				Attributes: make(map[string]string),
			},
			cfg:      &config.Config{},
			perKey:   []string{"model-a", "MODEL-A", "model-b", "model-a"},
			authKind: "apikey",
			wantHash: true,
			wantKind: "apikey",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ApplyAuthExcludedModelsMeta(tt.auth, tt.cfg, tt.perKey, tt.authKind)

			if tt.auth != nil && tt.cfg != nil {
				if tt.wantHash {
					if _, ok := tt.auth.Attributes["excluded_models_hash"]; !ok {
						t.Error("expected excluded_models_hash in attributes")
					}
				}
				if tt.wantKind != "" {
					if got := tt.auth.Attributes["auth_kind"]; got != tt.wantKind {
						t.Errorf("expected auth_kind=%s, got %s", tt.wantKind, got)
					}
				}
			}
		})
	}
}

func TestAddConfigHeadersToAttrs(t *testing.T) {
	tests := []struct {
		name    string
		headers map[string]string
		attrs   map[string]string
		want    map[string]string
	}{
		{
			name: "basic headers",
			headers: map[string]string{
				"Authorization": "Bearer token",
				"X-Custom":      "value",
			},
			attrs: map[string]string{"existing": "key"},
			want: map[string]string{
				"existing":             "key",
				"header:Authorization": "Bearer token",
				"header:X-Custom":      "value",
			},
		},
		{
			name:    "empty headers",
			headers: map[string]string{},
			attrs:   map[string]string{"existing": "key"},
			want:    map[string]string{"existing": "key"},
		},
		{
			name:    "nil headers",
			headers: nil,
			attrs:   map[string]string{"existing": "key"},
			want:    map[string]string{"existing": "key"},
		},
		{
			name:    "nil attrs",
			headers: map[string]string{"key": "value"},
			attrs:   nil,
			want:    nil,
		},
		{
			name: "skip empty keys and values",
			headers: map[string]string{
				"":      "value",
				"key":   "",
				"  ":    "value",
				"valid": "valid-value",
			},
			attrs: make(map[string]string),
			want: map[string]string{
				"header:valid": "valid-value",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			addConfigHeadersToAttrs(tt.headers, tt.attrs)
			if !reflect.DeepEqual(tt.attrs, tt.want) {
				t.Errorf("expected %v, got %v", tt.want, tt.attrs)
			}
		})
	}
}
