package locale

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestLoadFromJSON(t *testing.T) {
	tests := []struct {
		name      string
		jsonData  []byte
		wantError bool
		wantCount int
	}{
		{
			name: "valid JSON",
			jsonData: []byte(`[
				{
					"language_name": "English",
					"language_local": "English",
					"language_code": "en",
					"language_code2": "eng",
					"language_code3": "eng",
					"regions": [
						{
							"locale": "en-US",
							"country_name": "United States",
							"country_local": "United States",
							"country_code": "US",
							"flag": "🇺🇸"
						}
					]
				}
			]`),
			wantError: false,
			wantCount: 1,
		},
		{
			name: "multiple locales",
			jsonData: []byte(`[
				{
					"language_name": "English",
					"language_local": "English",
					"language_code": "en",
					"language_code2": "eng",
					"language_code3": "eng",
					"regions": []
				},
				{
					"language_name": "Spanish",
					"language_local": "Español",
					"language_code": "es",
					"language_code2": "spa",
					"language_code3": "spa",
					"regions": []
				}
			]`),
			wantError: false,
			wantCount: 2,
		},
		{
			name:      "invalid JSON",
			jsonData:  []byte(`{invalid json`),
			wantError: true,
			wantCount: 0,
		},
		{
			name:      "empty JSON",
			jsonData:  []byte(`[]`),
			wantError: false,
			wantCount: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := LoadFromJSON(tt.jsonData)

			if tt.wantError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Len(t, got, tt.wantCount)
			}
		})
	}
}

func TestFindByLanguageCode(t *testing.T) {
	testData := []LocaleEntry{
		{
			LanguageName:  "English",
			LanguageLocal: "English",
			LanguageCode:  "en",
			Regions:       []RegionEntry{},
		},
		{
			LanguageName:  "Spanish",
			LanguageLocal: "Español",
			LanguageCode:  "es",
			Regions:       []RegionEntry{},
		},
		{
			LanguageName:  "French",
			LanguageLocal: "Français",
			LanguageCode:  "fr",
			Regions:       []RegionEntry{},
		},
	}

	tests := []struct {
		name      string
		code      string
		wantFound bool
		wantName  string
	}{
		{
			name:      "find English",
			code:      "en",
			wantFound: true,
			wantName:  "English",
		},
		{
			name:      "find Spanish",
			code:      "es",
			wantFound: true,
			wantName:  "Spanish",
		},
		{
			name:      "find French",
			code:      "fr",
			wantFound: true,
			wantName:  "French",
		},
		{
			name:      "non-existent code",
			code:      "xx",
			wantFound: false,
			wantName:  "",
		},
		{
			name:      "empty code",
			code:      "",
			wantFound: false,
			wantName:  "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := FindByLanguageCode(tt.code, testData)

			if tt.wantFound {
				assert.NotNil(t, got)
				assert.Equal(t, tt.wantName, got.LanguageName)
			} else {
				assert.Nil(t, got)
			}
		})
	}
}

func TestFindByLocale(t *testing.T) {
	testData := []LocaleEntry{
		{
			LanguageName:  "English",
			LanguageLocal: "English",
			LanguageCode:  "en",
			Regions: []RegionEntry{
				{
					Locale:      "en-US",
					CountryName: "United States",
					CountryCode: "US",
					Flag:        "🇺🇸",
				},
				{
					Locale:      "en-GB",
					CountryName: "United Kingdom",
					CountryCode: "GB",
					Flag:        "🇬🇧",
				},
			},
		},
		{
			LanguageName:  "Spanish",
			LanguageLocal: "Español",
			LanguageCode:  "es",
			Regions: []RegionEntry{
				{
					Locale:      "es-ES",
					CountryName: "Spain",
					CountryCode: "ES",
					Flag:        "🇪🇸",
				},
			},
		},
	}

	tests := []struct {
		name            string
		locale          string
		wantFound       bool
		wantLanguage    string
		wantCountry     string
		wantCountryCode string
		wantFlag        string
	}{
		{
			name:            "find en-US",
			locale:          "en-US",
			wantFound:       true,
			wantLanguage:    "English",
			wantCountry:     "United States",
			wantCountryCode: "US",
			wantFlag:        "🇺🇸",
		},
		{
			name:            "find en-GB",
			locale:          "en-GB",
			wantFound:       true,
			wantLanguage:    "English",
			wantCountry:     "United Kingdom",
			wantCountryCode: "GB",
			wantFlag:        "🇬🇧",
		},
		{
			name:            "find es-ES",
			locale:          "es-ES",
			wantFound:       true,
			wantLanguage:    "Spanish",
			wantCountry:     "Spain",
			wantCountryCode: "ES",
			wantFlag:        "🇪🇸",
		},
		{
			name:      "non-existent locale",
			locale:    "xx-XX",
			wantFound: false,
		},
		{
			name:      "empty locale",
			locale:    "",
			wantFound: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotLang, gotRegion := FindByLocale(tt.locale, testData)

			if tt.wantFound {
				assert.NotNil(t, gotLang)
				assert.NotNil(t, gotRegion)
				assert.Equal(t, tt.wantLanguage, gotLang.LanguageName)
				assert.Equal(t, tt.wantCountry, gotRegion.CountryName)
				assert.Equal(t, tt.wantCountryCode, gotRegion.CountryCode)
				assert.Equal(t, tt.wantFlag, gotRegion.Flag)
			} else {
				assert.Nil(t, gotLang)
				assert.Nil(t, gotRegion)
			}
		})
	}
}

func TestGetMajorLanguages(t *testing.T) {
	testData := []LocaleEntry{
		{
			LanguageName:  "English",
			LanguageLocal: "English",
			LanguageCode:  "en",
			Regions:       []RegionEntry{},
		},
		{
			LanguageName:  "Spanish",
			LanguageLocal: "Español",
			LanguageCode:  "es",
			Regions:       []RegionEntry{},
		},
	}

	t.Run("get major languages", func(t *testing.T) {
		got := GetMajorLanguages(testData)
		assert.Len(t, got, 2)
		assert.Equal(t, testData[0].LanguageName, got[0].LanguageName)
		assert.Equal(t, testData[1].LanguageName, got[1].LanguageName)
	})

	t.Run("empty languages list", func(t *testing.T) {
		got := GetMajorLanguages([]LocaleEntry{})
		assert.Empty(t, got)
	})
}

func TestLocaleEntry(t *testing.T) {
	t.Run("locale entry fields", func(t *testing.T) {
		entry := LocaleEntry{
			LanguageName:  "English",
			LanguageLocal: "English",
			LanguageCode:  "en",
			LanguageCode2: "eng",
			LanguageCode3: "eng",
			Regions: []RegionEntry{
				{
					Locale:       "en-US",
					CountryName:  "United States",
					CountryLocal: "United States",
					CountryCode:  "US",
					Flag:         "🇺🇸",
				},
			},
		}

		assert.Equal(t, "English", entry.LanguageName)
		assert.Equal(t, "English", entry.LanguageLocal)
		assert.Equal(t, "en", entry.LanguageCode)
		assert.Equal(t, "eng", entry.LanguageCode2)
		assert.Equal(t, "eng", entry.LanguageCode3)
		assert.Len(t, entry.Regions, 1)
		assert.Equal(t, "en-US", entry.Regions[0].Locale)
		assert.Equal(t, "United States", entry.Regions[0].CountryName)
		assert.Equal(t, "US", entry.Regions[0].CountryCode)
		assert.Equal(t, "🇺🇸", entry.Regions[0].Flag)
	})
}

func TestRegionEntry(t *testing.T) {
	t.Run("region entry fields", func(t *testing.T) {
		entry := RegionEntry{
			Locale:       "en-US",
			CountryName:  "United States",
			CountryLocal: "United States",
			CountryCode:  "US",
			Flag:         "🇺🇸",
		}

		assert.Equal(t, "en-US", entry.Locale)
		assert.Equal(t, "United States", entry.CountryName)
		assert.Equal(t, "United States", entry.CountryLocal)
		assert.Equal(t, "US", entry.CountryCode)
		assert.Equal(t, "🇺🇸", entry.Flag)
	})
}

func TestJSONUnmarshal(t *testing.T) {
	t.Run("unmarshal locale entry", func(t *testing.T) {
		jsonData := `{
			"language_name": "English",
			"language_local": "English",
			"language_code": "en",
			"language_code2": "eng",
			"language_code3": "eng",
			"regions": [
				{
					"locale": "en-US",
					"country_name": "United States",
					"country_local": "United States",
					"country_code": "US",
					"flag": "🇺🇸"
				}
			]
		}`

		var entry LocaleEntry
		err := json.Unmarshal([]byte(jsonData), &entry)
		assert.NoError(t, err)
		assert.Equal(t, "English", entry.LanguageName)
		assert.Equal(t, "en", entry.LanguageCode)
		assert.Len(t, entry.Regions, 1)
		assert.Equal(t, "en-US", entry.Regions[0].Locale)
	})

	t.Run("unmarshal region entry", func(t *testing.T) {
		jsonData := `{
			"locale": "en-US",
			"country_name": "United States",
			"country_local": "United States",
			"country_code": "US",
			"flag": "🇺🇸"
		}`

		var entry RegionEntry
		err := json.Unmarshal([]byte(jsonData), &entry)
		assert.NoError(t, err)
		assert.Equal(t, "en-US", entry.Locale)
		assert.Equal(t, "United States", entry.CountryName)
		assert.Equal(t, "US", entry.CountryCode)
		assert.Equal(t, "🇺🇸", entry.Flag)
	})
}
