package i18n

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

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestInit(t *testing.T) {
	err := Init()
	require.NoError(t, err)
	assert.NotNil(t, Bundle)
}

func TestTSimple_EnglishKnownKey(t *testing.T) {
	require.NoError(t, Init())

	localizer := NewLocalizer("en")
	msg := TSimple(localizer, "error.task_not_found")
	assert.Equal(t, "Task not found", msg)
}

func TestTSimple_SpanishKnownKey(t *testing.T) {
	require.NoError(t, Init())

	localizer := NewLocalizer("es")
	msg := TSimple(localizer, "error.invalid_credentials")
	assert.Equal(t, "Credenciales no validas", msg)
}

func TestT_WithTemplateData(t *testing.T) {
	require.NoError(t, Init())

	localizer := NewLocalizer("en")
	msg := T(localizer, "message.tasks_report_sent", map[string]interface{}{
		"Email": "test@example.com",
	})
	assert.Contains(t, msg, "test@example.com")
}

func TestTSimple_UnknownKeyReturnsKey(t *testing.T) {
	require.NoError(t, Init())

	localizer := NewLocalizer("en")
	key := "error.nonexistent_key_that_does_not_exist"
	msg := TSimple(localizer, key)
	assert.Equal(t, key, msg)
}

func TestTSimple_FallbackToEnglish(t *testing.T) {
	require.NoError(t, Init())

	// Use a language that may not have all translations — fallback to English
	localizer := NewLocalizer("xx", "en")
	msg := TSimple(localizer, "error.task_not_found")
	assert.Equal(t, "Task not found", msg)
}

func TestT_NilLocalizer_UsesDefault(t *testing.T) {
	require.NoError(t, Init())

	msg := T(nil, "error.task_not_found", nil)
	assert.Equal(t, "Task not found", msg)
}

func TestNewLocalizer(t *testing.T) {
	require.NoError(t, Init())

	localizer := NewLocalizer("en")
	assert.NotNil(t, localizer)
}

func TestParseAcceptLanguage(t *testing.T) {
	tests := []struct {
		name     string
		header   string
		expected []string
	}{
		{"empty returns default", "", []string{"en"}},
		{"english", "en-US,en;q=0.9", []string{"en", "en"}},
		{"spanish first", "es,en;q=0.5", []string{"es", "en"}},
		{"unsupported returns default", "xx-YY", []string{"en"}},
		{"french", "fr-FR,fr;q=0.9,en;q=0.5", []string{"fr", "fr", "en"}},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := parseAcceptLanguage(tc.header)
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestMatchLocale(t *testing.T) {
	tests := []struct {
		name     string
		langs    []string
		expected string
	}{
		{"finds supported", []string{"es", "en"}, "es"},
		{"first match wins", []string{"fr", "de"}, "fr"},
		{"unsupported returns default", []string{"xx"}, "en"},
		{"empty returns default", []string{}, "en"},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			result := matchLocale(tc.langs)
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestMiddleware_SetsLocalizerAndLocale(t *testing.T) {
	require.NoError(t, Init())

	e := echo.New()
	req := httptest.NewRequest(http.MethodGet, "/", nil)
	req.Header.Set("Accept-Language", "es,en;q=0.5")
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)

	handler := Middleware()(func(c echo.Context) error {
		// Verify localizer is set
		localizer := GetLocalizer(c)
		assert.NotNil(t, localizer)

		// Verify locale is set
		locale := GetLocale(c)
		assert.Equal(t, "es", locale)

		return nil
	})

	err := handler(c)
	assert.NoError(t, err)
}

func TestGetLocalizer_NoContextValue_ReturnsDefault(t *testing.T) {
	require.NoError(t, Init())

	e := echo.New()
	req := httptest.NewRequest(http.MethodGet, "/", nil)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)

	localizer := GetLocalizer(c)
	assert.NotNil(t, localizer)
}

func TestGetLocale_NoContextValue_ReturnsDefault(t *testing.T) {
	e := echo.New()
	req := httptest.NewRequest(http.MethodGet, "/", nil)
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)

	locale := GetLocale(c)
	assert.Equal(t, "en", locale)
}

func TestLocalizedMessage(t *testing.T) {
	require.NoError(t, Init())

	e := echo.New()
	req := httptest.NewRequest(http.MethodGet, "/", nil)
	req.Header.Set("Accept-Language", "en")
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)

	// Set up localizer through middleware
	handler := Middleware()(func(c echo.Context) error {
		msg := LocalizedMessage(c, "error.task_not_found")
		assert.Equal(t, "Task not found", msg)
		return nil
	})

	err := handler(c)
	assert.NoError(t, err)
}

func TestLocalizedMessageWithData(t *testing.T) {
	require.NoError(t, Init())

	e := echo.New()
	req := httptest.NewRequest(http.MethodGet, "/", nil)
	req.Header.Set("Accept-Language", "en")
	rec := httptest.NewRecorder()
	c := e.NewContext(req, rec)

	handler := Middleware()(func(c echo.Context) error {
		msg := LocalizedMessageWithData(c, "message.tasks_report_sent", map[string]interface{}{
			"Email": "user@example.com",
		})
		assert.Contains(t, msg, "user@example.com")
		return nil
	})

	err := handler(c)
	assert.NoError(t, err)
}

func TestSupportedLanguages(t *testing.T) {
	assert.Contains(t, SupportedLanguages, "en")
	assert.Contains(t, SupportedLanguages, "es")
	assert.Contains(t, SupportedLanguages, "fr")
	assert.Equal(t, "en", DefaultLanguage)
}
