package i18nx

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
)

func TestNormalizeLocale(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name string
		in   string
		want string
	}{
		{name: "default for blank", in: "", want: LocaleEnUS},
		{name: "exact chinese", in: "zh-CN", want: LocaleZhCN},
		{name: "underscore chinese", in: "zh_CN", want: LocaleZhCN},
		{name: "short chinese", in: "zh", want: LocaleZhCN},
		{name: "exact english", in: "en-US", want: LocaleEnUS},
		{name: "underscore english", in: "en_US", want: LocaleEnUS},
		{name: "short english", in: "en", want: LocaleEnUS},
		{name: "unsupported falls back", in: "fr-FR", want: LocaleEnUS},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := NormalizeLocale(tt.in); got != tt.want {
				t.Fatalf("NormalizeLocale(%q) = %q, want %q", tt.in, got, tt.want)
			}
		})
	}
}

func TestResolveLocaleFromHeaders(t *testing.T) {
	t.Parallel()

	req := httptest.NewRequest(http.MethodGet, "/api/dashboard/user/list", nil)
	req.Header.Set("Accept-Language", "fr-FR, en-US;q=0.9, zh-CN;q=0.8")

	if got := ResolveRequestLocale(req); got != LocaleEnUS {
		t.Fatalf("ResolveRequestLocale() = %q, want %q", got, LocaleEnUS)
	}
}

func TestResolveLocalePrefersXLocale(t *testing.T) {
	t.Parallel()

	req := httptest.NewRequest(http.MethodGet, "/api/dashboard/user/list", nil)
	req.Header.Set("X-Locale", "en-US")
	req.Header.Set("Accept-Language", "zh-CN")

	if got := ResolveRequestLocale(req); got != LocaleEnUS {
		t.Fatalf("ResolveRequestLocale() = %q, want %q", got, LocaleEnUS)
	}
}

func TestTranslateFallsBackToEnglish(t *testing.T) {
	t.Parallel()

	if got := TLocale(LocaleEnUS, "error.auth.expired"); got != "Your session has expired. Please sign in again." {
		t.Fatalf("english translation = %q", got)
	}
	if got := TLocale("fr-FR", "error.auth.expired"); got != "Your session has expired. Please sign in again." {
		t.Fatalf("fallback translation = %q", got)
	}
}

func TestGetfSupportsLocaleKeys(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name   string
		locale string
		key    string
		args   []any
		want   string
	}{
		{
			name:   "batch limit",
			locale: LocaleEnUS,
			key:    "error.agentTeamSchedule.batchLimit",
			args:   []any{31},
			want:   "You can generate at most 31 schedule entries at a time.",
		},
		{
			name:   "directory not found",
			locale: LocaleEnUS,
			key:    "error.e0273",
			want:   "Directory not found.",
		},
		{
			name:   "single knowledge base reference",
			locale: LocaleEnUS,
			key:    "error.knowledgeBase.referencedByAgent",
			args:   []any{"SalesBot"},
			want:   "This knowledge base is referenced by AI Agent \"SalesBot\". Remove the binding first.",
		},
		{
			name:   "multiple knowledge base references",
			locale: LocaleEnUS,
			key:    "error.knowledgeBase.referencedByAgents",
			args:   []any{3},
			want:   "This knowledge base is referenced by 3 AI Agents. Remove the bindings first.",
		},
		{
			name:   "faq import duplicated question",
			locale: LocaleEnUS,
			key:    "error.knowledgeFAQImport.duplicateQuestionInFile",
			args:   []any{12},
			want:   "The standard question is duplicated in the same file. It first appeared on row 12.",
		},
		{
			name:   "zh keeps chinese value",
			locale: LocaleZhCN,
			key:    "error.e0273",
			want:   "目录不存在",
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			if got := Getf(tt.locale, tt.key, tt.args...); got != tt.want {
				t.Fatalf("Getf() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestGetfFallsBackToKey(t *testing.T) {
	t.Parallel()

	if got := Getf(LocaleEnUS, "missing.key"); got != "missing.key" {
		t.Fatalf("missing translation = %q, want %q", got, "missing.key")
	}
}

func TestMiddlewareStoresLocale(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(Middleware())
	router.GET("/ping", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, Locale(ctx))
	})

	req := httptest.NewRequest(http.MethodGet, "/ping", nil)
	req.Header.Set("X-Locale", "en-US")
	recorder := httptest.NewRecorder()

	router.ServeHTTP(recorder, req)

	if recorder.Code != http.StatusOK {
		t.Fatalf("status = %d, want %d", recorder.Code, http.StatusOK)
	}
	if got := recorder.Body.String(); got != LocaleEnUS {
		t.Fatalf("middleware locale = %q, want %q", got, LocaleEnUS)
	}
}
