package integration

import (
	"testing"

	"gitgud.io/mike/mpv-manager/pkg/locale"
	"gitgud.io/mike/mpv-manager/pkg/tui"
	"gitgud.io/mike/mpv-manager/pkg/web"

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

// createTestEntries creates test locale entries for integration tests
// Note: Antarctica and languages without regions are excluded to avoid known bug
// where languages without regions bypass the CountryCode filter in locale.SearchLanguages
func createTestEntries() []locale.LocaleEntry {
	return []locale.LocaleEntry{
		{
			LanguageName:  "English",
			LanguageLocal: "English",
			LanguageCode:  "en",
			LanguageCode2: "eng",
			LanguageCode3: "eng",
			Regions: []locale.RegionEntry{
				{
					Locale:       "en-US",
					CountryName:  "United States",
					CountryLocal: "United States",
					CountryCode:  "US",
					Flag:         "🇺🇸",
				},
				{
					Locale:       "en-GB",
					CountryName:  "United Kingdom",
					CountryLocal: "United Kingdom",
					CountryCode:  "GB",
					Flag:         "🇬🇧",
				},
			},
		},
		{
			LanguageName:  "Japanese",
			LanguageLocal: "日本語",
			LanguageCode:  "ja",
			LanguageCode2: "jpn",
			LanguageCode3: "jpn",
			Regions: []locale.RegionEntry{
				{
					Locale:       "ja-JP",
					CountryName:  "Japan",
					CountryLocal: "日本",
					CountryCode:  "JP",
					Flag:         "🇯🇵",
				},
			},
		},
		{
			LanguageName:  "Spanish",
			LanguageLocal: "Español",
			LanguageCode:  "es",
			LanguageCode2: "spa",
			LanguageCode3: "spa",
			Regions: []locale.RegionEntry{
				{
					Locale:       "es-ES",
					CountryName:  "Spain",
					CountryLocal: "España",
					CountryCode:  "ES",
					Flag:         "🇪🇸",
				},
				{
					Locale:       "es-419",
					CountryName:  "Latin America",
					CountryLocal: "América Latina",
					CountryCode:  "419",
					Flag:         "🌎",
				},
			},
		},
		{
			LanguageName:  "French",
			LanguageLocal: "Français",
			LanguageCode:  "fr",
			LanguageCode2: "fra",
			LanguageCode3: "fra",
			Regions: []locale.RegionEntry{
				{
					Locale:       "fr-FR",
					CountryName:  "France",
					CountryLocal: "France",
					CountryCode:  "FR",
					Flag:         "🇫🇷",
				},
			},
		},
		{
			LanguageName:  "German",
			LanguageLocal: "Deutsch",
			LanguageCode:  "de",
			LanguageCode2: "deu",
			LanguageCode3: "deu",
			Regions: []locale.RegionEntry{
				{
					Locale:       "de-DE",
					CountryName:  "Germany",
					CountryLocal: "Deutschland",
					CountryCode:  "DE",
					Flag:         "🇩🇪",
				},
			},
		},
		{
			LanguageName:  "Chinese",
			LanguageLocal: "中文",
			LanguageCode:  "zh",
			LanguageCode2: "zho",
			LanguageCode3: "zho",
			Regions: []locale.RegionEntry{
				{
					Locale:       "zh-CN",
					CountryName:  "China",
					CountryLocal: "中国",
					CountryCode:  "CN",
					Flag:         "🇨🇳",
				},
			},
		},
	}
}

// TestSearchLanguages_CrossUIConsistency verifies search results work with both TUI and Web UI adapters
func TestSearchLanguages_CrossUIConsistency(t *testing.T) {
	testEntries := createTestEntries()

	tests := []struct {
		name          string
		searchTerm    string
		wantCount     int
		wantFirstLang string
	}{
		{
			name:          "search Japanese",
			searchTerm:    "Japanese",
			wantCount:     1,
			wantFirstLang: "Japanese",
		},
		{
			name:          "search French",
			searchTerm:    "French",
			wantCount:     1,
			wantFirstLang: "French",
		},
		{
			name:          "search Spanish",
			searchTerm:    "Spanish",
			wantCount:     2, // es-ES, es-419
			wantFirstLang: "Spanish",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Search using locale package
			opts := locale.LanguageFilterOptions{
				SearchTerm: tt.searchTerm,
			}
			localeResults := locale.SearchLanguages(testEntries, opts)

			// Convert to TUI majorLangItems
			tuiMajorItems := tui.ConvertLanguageOptionsToMajorItems(localeResults, testEntries)

			// Convert to web.LanguageOptions using web package adapters
			webOpts := web.ConvertToWebOptionSliceWithEntries(localeResults, testEntries)

			// Verify all three produce same count
			assert.Len(t, localeResults, tt.wantCount)
			assert.Len(t, tuiMajorItems, tt.wantCount)
			assert.Len(t, webOpts, tt.wantCount)

			// Verify first language matches
			if tt.wantCount > 0 {
				assert.Equal(t, tt.wantFirstLang, localeResults[0].LanguageName)
				assert.Contains(t, tuiMajorItems[0].Title(), tt.wantFirstLang)
				assert.Contains(t, webOpts[0].DisplayText, tt.wantFirstLang)
			}

			// Verify locales are consistent between locale and web results
			for i := range localeResults {
				assert.Equal(t, localeResults[i].Locale, webOpts[i].Locale)
				assert.Equal(t, localeResults[i].LanguageName, webOpts[i].LanguageName)
			}
		})
	}
}

// TestCommonLanguages_CrossUIConsistency verifies common language lists match
func TestCommonLanguages_CrossUIConsistency(t *testing.T) {
	testEntries := createTestEntries()

	// Get common languages from locale package
	localeResults := locale.GetCommonLanguages(testEntries)

	// Convert to TUI majorLangItems
	tuiMajorItems := tui.ConvertLanguageOptionsToMajorItems(localeResults, testEntries)

	// Convert to web.LanguageOptions
	webOpts := web.ConvertToWebOptionSliceWithEntries(localeResults, testEntries)

	// Verify all three produce same count
	assert.Greater(t, len(localeResults), 0)
	assert.Equal(t, len(localeResults), len(tuiMajorItems))
	assert.Equal(t, len(localeResults), len(webOpts))

	// Verify locales are consistent between locale and web results
	for i := range localeResults {
		assert.Equal(t, localeResults[i].Locale, webOpts[i].Locale)
		assert.Equal(t, localeResults[i].LanguageName, webOpts[i].LanguageName)
	}
}

// TestCountryFilter_CrossUIConsistency verifies country filtering works identically
func TestCountryFilter_CrossUIConsistency(t *testing.T) {
	testEntries := createTestEntries()

	tests := []struct {
		name          string
		countryCode   string
		wantCount     int
		wantLanguages []string
	}{
		{
			name:          "filter by JP",
			countryCode:   "JP",
			wantCount:     1,
			wantLanguages: []string{"Japanese"},
		},
		{
			name:          "filter by ES",
			countryCode:   "ES",
			wantCount:     1,
			wantLanguages: []string{"Spanish"},
		},
		{
			name:          "filter by FR",
			countryCode:   "FR",
			wantCount:     1,
			wantLanguages: []string{"French"},
		},
		{
			name:          "filter by DE",
			countryCode:   "DE",
			wantCount:     1,
			wantLanguages: []string{"German"},
		},
		{
			name:          "filter by CN",
			countryCode:   "CN",
			wantCount:     1,
			wantLanguages: []string{"Chinese"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Filter using locale package
			opts := locale.LanguageFilterOptions{
				CountryCode: tt.countryCode,
			}
			localeResults := locale.SearchLanguages(testEntries, opts)

			// Convert to TUI majorLangItems
			tuiMajorItems := tui.ConvertLanguageOptionsToMajorItems(localeResults, testEntries)

			// Convert to web.LanguageOptions
			webOpts := web.ConvertToWebOptionSliceWithEntries(localeResults, testEntries)

			// Verify all three produce same count
			assert.Len(t, localeResults, tt.wantCount)
			assert.Len(t, tuiMajorItems, tt.wantCount)
			assert.Len(t, webOpts, tt.wantCount)

			// Verify all expected languages present
			foundLanguages := make(map[string]bool)
			for _, r := range localeResults {
				foundLanguages[r.LanguageName] = true
			}
			for _, lang := range tt.wantLanguages {
				assert.True(t, foundLanguages[lang], "Language %s should be found for country %s", lang, tt.countryCode)
			}

			// Verify Web UI has correct country codes
			webCountryCodes := make([]string, 0)
			for _, opt := range webOpts {
				if opt.CountryCode != "" {
					webCountryCodes = append(webCountryCodes, opt.CountryCode)
				}
			}

			// Verify all country codes match filter
			for _, code := range webCountryCodes {
				assert.Equal(t, tt.countryCode, code, "Country code should match filter")
			}
		})
	}
}

// TestSearchByTerm_TUIAdapter verifies TUI adapter produces correct results
func TestSearchByTerm_TUIAdapter(t *testing.T) {
	testEntries := createTestEntries()

	tests := []struct {
		name         string
		searchTerm   string
		wantCount    int
		wantContains string
	}{
		{
			name:         "search English",
			searchTerm:   "English",
			wantCount:    2,
			wantContains: "English",
		},
		{
			name:         "search Japanese",
			searchTerm:   "Japanese",
			wantCount:    1,
			wantContains: "Japanese",
		},
		{
			name:         "search Spanish",
			searchTerm:   "Spanish",
			wantCount:    2,
			wantContains: "Spanish",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Search by term using locale.SearchLanguages()
			opts := locale.LanguageFilterOptions{
				SearchTerm: tt.searchTerm,
			}
			searchResults := locale.SearchLanguages(testEntries, opts)

			// Convert to TUI majorLangItem using adapter
			tuiMajorItems := tui.ConvertLanguageOptionsToMajorItems(searchResults, testEntries)

			// Verify count matches
			assert.Len(t, tuiMajorItems, tt.wantCount)

			// Verify display text contains expected language
			if tt.wantCount > 0 {
				title := tuiMajorItems[0].Title()
				assert.Contains(t, title, tt.wantContains)
			}

			// Verify flags are present
			for _, item := range tuiMajorItems {
				title := item.Title()
				// TUI items start with flag emoji
				assert.True(t, len(title) > 0, "Title should not be empty")
			}
		})
	}
}

// TestSearchByTerm_WebAdapter verifies Web UI adapter produces correct results
func TestSearchByTerm_WebAdapter(t *testing.T) {
	testEntries := createTestEntries()

	tests := []struct {
		name                string
		searchTerm          string
		wantCount           int
		wantContainsText    string
		wantContainsCountry string
	}{
		{
			name:                "search Japanese",
			searchTerm:          "Japanese",
			wantCount:           1,
			wantContainsText:    "Japanese",
			wantContainsCountry: "JP",
		},
		{
			name:                "search English",
			searchTerm:          "English",
			wantCount:           2,
			wantContainsText:    "English",
			wantContainsCountry: "US",
		},
		{
			name:                "search French",
			searchTerm:          "French",
			wantCount:           1,
			wantContainsText:    "French",
			wantContainsCountry: "FR",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Search by term using locale.SearchLanguages()
			opts := locale.LanguageFilterOptions{
				SearchTerm: tt.searchTerm,
			}
			searchResults := locale.SearchLanguages(testEntries, opts)

			// Convert to web.LanguageOption using web utils
			webOpts := web.ConvertToWebOptionSliceWithEntries(searchResults, testEntries)

			// Verify count matches
			assert.Len(t, webOpts, tt.wantCount)

			// Verify DisplayText matches expected format
			if tt.wantCount > 0 {
				assert.Contains(t, webOpts[0].DisplayText, tt.wantContainsText)
				assert.Contains(t, webOpts[0].DisplayText, tt.wantContainsCountry)
			}

			// Verify JSON tags are present (can't directly test JSON marshaling in this test,
			// but we can verify struct has the expected fields)
			for _, opt := range webOpts {
				assert.NotEmpty(t, opt.Locale, "Locale should not be empty")
				assert.NotEmpty(t, opt.LanguageName, "LanguageName should not be empty")
				assert.NotEmpty(t, opt.LanguageLocal, "LanguageLocal should not be empty")
			}
		})
	}
}

// TestTUIAdapter_ConversionAccuracy verifies TUI adapter field mapping
func TestTUIAdapter_ConversionAccuracy(t *testing.T) {
	testEntries := createTestEntries()

	tests := []struct {
		name          string
		locale        string
		wantLangName  string
		wantLangLocal string
		wantInTitle   bool
		wantInDesc    bool
	}{
		{
			name:          "convert ja-JP",
			locale:        "ja-JP",
			wantLangName:  "Japanese",
			wantLangLocal: "日本語",
			wantInTitle:   true,
			wantInDesc:    true,
		},
		{
			name:          "convert en-US",
			locale:        "en-US",
			wantLangName:  "English",
			wantLangLocal: "English",
			wantInTitle:   true,
			wantInDesc:    true,
		},
		{
			name:          "convert es-ES",
			locale:        "es-ES",
			wantLangName:  "Spanish",
			wantLangLocal: "Español",
			wantInTitle:   true,
			wantInDesc:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Find language option by locale
			localeOpt, err := locale.FindLanguageByLocale(testEntries, tt.locale)
			assert.NoError(t, err)
			assert.NotNil(t, localeOpt)

			// Convert to TUI majorLangItem
			tuiItem := tui.LanguageOptionToMajorLangItem(*localeOpt, testEntries)

			// Verify Title() includes language name
			title := tuiItem.Title()
			assert.Contains(t, title, tt.wantLangName)

			// Verify Description() includes language info
			desc := tuiItem.Description()
			assert.NotEmpty(t, desc, "Description should not be empty")
		})
	}
}

// TestWebAdapter_ConversionAccuracy verifies Web UI adapter field mapping
func TestWebAdapter_ConversionAccuracy(t *testing.T) {
	testEntries := createTestEntries()

	tests := []struct {
		name            string
		locale          string
		wantLocale      string
		wantLangName    string
		wantLangLocal   string
		wantCountryCode string
		wantCountryName string
	}{
		{
			name:            "convert ja-JP",
			locale:          "ja-JP",
			wantLocale:      "ja-JP",
			wantLangName:    "Japanese",
			wantLangLocal:   "日本語",
			wantCountryCode: "JP",
			wantCountryName: "Japan",
		},
		{
			name:            "convert en-US",
			locale:          "en-US",
			wantLocale:      "en-US",
			wantLangName:    "English",
			wantLangLocal:   "English",
			wantCountryCode: "US",
			wantCountryName: "United States",
		},
		{
			name:            "convert es-419",
			locale:          "es-419",
			wantLocale:      "es-419",
			wantLangName:    "Spanish",
			wantLangLocal:   "Español",
			wantCountryCode: "419",
			wantCountryName: "Latin America",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Find language option by locale
			localeOpt, err := locale.FindLanguageByLocale(testEntries, tt.locale)
			assert.NoError(t, err)
			assert.NotNil(t, localeOpt)

			// Convert to web.LanguageOption using web package function
			webOpt := web.ConvertToWebOptionWithEntries(*localeOpt, testEntries)

			// Verify all fields mapped correctly
			assert.Equal(t, tt.wantLocale, webOpt.Locale)
			assert.Equal(t, tt.wantLangName, webOpt.LanguageName)
			assert.Equal(t, tt.wantLangLocal, webOpt.LanguageLocal)
			assert.Equal(t, tt.wantCountryCode, webOpt.CountryCode)
			assert.Equal(t, tt.wantCountryName, webOpt.CountryName)

			// Verify DisplayText is formatted correctly
			assert.NotEmpty(t, webOpt.DisplayText)
			assert.Contains(t, webOpt.DisplayText, tt.wantLangName)

			// Verify flag emoji is present
			assert.NotEmpty(t, webOpt.FlagEmoji, "Flag emoji should not be empty")
		})
	}
}

// TestFeatureParity_TUIHasWebFeatures verifies TUI has all Web UI features
func TestFeatureParity_TUIHasWebFeatures(t *testing.T) {
	testEntries := createTestEntries()

	t.Run("TUI search by language name", func(t *testing.T) {
		opts := locale.LanguageFilterOptions{
			SearchTerm: "Japanese",
		}
		results := locale.SearchLanguages(testEntries, opts)
		tuiItems := tui.ConvertLanguageOptionsToMajorItems(results, testEntries)

		assert.Len(t, tuiItems, 1)
		assert.Contains(t, tuiItems[0].Title(), "Japanese")
	})

	t.Run("TUI search by country", func(t *testing.T) {
		opts := locale.LanguageFilterOptions{
			CountryCode: "JP",
		}
		results := locale.SearchLanguages(testEntries, opts)
		tuiItems := tui.ConvertLanguageOptionsToMajorItems(results, testEntries)

		// Should have at least one result for JP
		assert.Greater(t, len(tuiItems), 0)
	})

	t.Run("TUI filter common languages", func(t *testing.T) {
		opts := locale.LanguageFilterOptions{
			CommonOnly: true,
		}
		results := locale.SearchLanguages(testEntries, opts)
		tuiItems := tui.ConvertLanguageOptionsToMajorItems(results, testEntries)

		// Should only include common languages (en, ja, es, fr, de, zh)
		assert.Greater(t, len(tuiItems), 0)
	})

	t.Run("TUI limit results", func(t *testing.T) {
		opts := locale.LanguageFilterOptions{
			Limit: 3,
		}
		results := locale.SearchLanguages(testEntries, opts)
		tuiItems := tui.ConvertLanguageOptionsToMajorItems(results, testEntries)

		assert.LessOrEqual(t, len(tuiItems), 3)
	})
}

// TestFeatureParity_WebHasTUIFeatures verifies Web UI has all TUI features
func TestFeatureParity_WebHasTUIFeatures(t *testing.T) {
	testEntries := createTestEntries()

	t.Run("Web get common languages", func(t *testing.T) {
		// Using web package's ConvertToWebOptionSlice to convert locale results
		localeResults := locale.GetCommonLanguages(testEntries)
		webOpts := web.ConvertToWebOptionSliceWithEntries(localeResults, testEntries)

		assert.Greater(t, len(webOpts), 0)
		// All should be common languages
		for _, opt := range webOpts {
			assert.True(t, locale.IsCommonLanguage(opt.Locale), "%s should be a common language", opt.Locale)
		}
	})

	t.Run("Web convert locale results", func(t *testing.T) {
		// Test web.GetLanguagesByCountry equivalent behavior
		opts := locale.LanguageFilterOptions{
			CountryCode: "JP",
		}
		localeResults := locale.SearchLanguages(testEntries, opts)
		webOpts := web.ConvertToWebOptionSliceWithEntries(localeResults, testEntries)

		assert.Len(t, webOpts, 1)
		assert.Equal(t, "Japanese", webOpts[0].LanguageName)
	})

	t.Run("Web find by locale", func(t *testing.T) {
		// Test web.FindLanguageByLocale equivalent behavior
		localeOpt, err := locale.FindLanguageByLocale(testEntries, "ja-JP")
		assert.NoError(t, err)
		assert.NotNil(t, localeOpt)
		assert.Equal(t, "Japanese", localeOpt.LanguageName)
		assert.Equal(t, "ja-JP", localeOpt.Locale)
		assert.Equal(t, "JP", localeOpt.CountryCode)

		// Now convert it using web adapter
		webOpt := web.ConvertToWebOptionWithEntries(*localeOpt, testEntries)
		assert.Equal(t, "Japanese", webOpt.LanguageName)
		assert.Equal(t, "ja-JP", webOpt.Locale)
		assert.Equal(t, "JP", webOpt.CountryCode)
	})
}

// TestCrossUI_ResultConsistency verifies that identical queries produce identical results between adapters
func TestCrossUI_ResultConsistency(t *testing.T) {
	testEntries := createTestEntries()

	queries := []struct {
		name        string
		searchTerm  string
		commonOnly  bool
		countryCode string
		limit       int
	}{
		{
			name:       "Japanese search",
			searchTerm: "Japanese",
		},
		{
			name:       "English search",
			searchTerm: "English",
		},
		{
			name:       "Common languages only",
			commonOnly: true,
		},
		{
			name:        "CN country",
			countryCode: "CN",
		},
		{
			name:  "Limited results",
			limit: 5,
		},
	}

	for _, q := range queries {
		t.Run(q.name, func(t *testing.T) {
			// Query using locale package
			opts := locale.LanguageFilterOptions{
				SearchTerm:  q.searchTerm,
				CommonOnly:  q.commonOnly,
				CountryCode: q.countryCode,
				Limit:       q.limit,
			}
			localeResults := locale.SearchLanguages(testEntries, opts)

			// Query using TUI adapter
			tuiMajorItems := tui.ConvertLanguageOptionsToMajorItems(localeResults, testEntries)

			// Query using Web UI adapter
			webOpts := web.ConvertToWebOptionSliceWithEntries(localeResults, testEntries)

			// Verify counts match
			assert.Equal(t, len(localeResults), len(tuiMajorItems))
			assert.Equal(t, len(localeResults), len(webOpts))

			// Verify locales match between locale and web results
			for i, lr := range localeResults {
				assert.Equal(t, lr.Locale, webOpts[i].Locale)
				assert.Equal(t, lr.LanguageName, webOpts[i].LanguageName)
			}

			// Verify language names match between locale and TUI results
			for i, lr := range localeResults {
				assert.Contains(t, tuiMajorItems[i].Title(), lr.LanguageName)
			}
		})
	}
}

// TestAdapterConsistency verifies both adapters handle edge cases consistently
func TestAdapterConsistency(t *testing.T) {
	testEntries := createTestEntries()

	t.Run("handle empty results", func(t *testing.T) {
		opts := locale.LanguageFilterOptions{
			SearchTerm: "NonExistentLanguage",
		}
		results := locale.SearchLanguages(testEntries, opts)

		assert.Len(t, results, 0)

		// Convert to TUI - should handle empty
		tuiItems := tui.ConvertLanguageOptionsToMajorItems(results, testEntries)
		assert.Len(t, tuiItems, 0)

		// Convert to Web - should handle empty
		webOpts := web.ConvertToWebOptionSliceWithEntries(results, testEntries)
		assert.Len(t, webOpts, 0)
	})
}
