package web

import (
	"fmt"
	"runtime"
	"sort"
	"strings"

	"gitgud.io/mike/mpv-manager/internal/assets"
	"gitgud.io/mike/mpv-manager/pkg/locale"
)

type LocaleCache struct {
	entries []locale.LocaleEntry
	loaded  bool
}

var localeCache = &LocaleCache{}

func LoadLocales() ([]locale.LocaleEntry, error) {
	if localeCache.loaded {
		return localeCache.entries, nil
	}

	jsonData, err := assets.ReadLocales()
	if err != nil {
		return nil, fmt.Errorf("failed to read locales.json: %w", err)
	}

	localeCache.entries = jsonData
	localeCache.loaded = true

	return jsonData, nil
}

func GetFlagDisplay(localeStr string) string {
	if localeStr == "" {
		return ""
	}

	if runtime.GOOS == "windows" {
		_, region := locale.FindByLocale(localeStr, localeCache.entries)
		if region != nil {
			return fmt.Sprintf("/static/flags/%s.png", region.CountryCode)
		}
		return ""
	}

	parts := strings.Split(localeStr, "-")
	if len(parts) > 0 {
		return locale.GetFlagForLanguageCode(strings.ToLower(parts[0]))
	}
	return "🏳️"
}

// GetLanguageEntry returns the locale entry for a language code
func GetLanguageEntry(localeStr string) *locale.LocaleEntry {
	if localeStr == "" {
		return nil
	}

	// Ensure locale cache is loaded
	if !localeCache.loaded {
		LoadLocales()
	}

	parts := strings.Split(localeStr, "-")
	langCode := strings.ToLower(parts[0])

	entry := locale.FindByLanguageCode(langCode, localeCache.entries)
	if entry == nil {
		return nil
	}

	return entry
}

// Default country codes for common languages when no region specified
var defaultCountryCodes = map[string]string{
	"en": "US", // English
	"es": "ES", // Spanish
	"ar": "SA", // Arabic
	"ja": "JP", // Japanese
	"zh": "CN", // Chinese
	"fr": "FR", // French
	"de": "DE", // German
	"ru": "RU", // Russian
	"pt": "BR", // Portuguese
	"it": "IT", // Italian
	"ko": "KR", // Korean
	"hi": "IN", // Hindi
	"nl": "NL", // Dutch
	"pl": "PL", // Polish
	"tr": "TR", // Turkish
	"vi": "VN", // Vietnamese
	"th": "TH", // Thai
	"id": "ID", // Indonesian
	"ms": "MY", // Malay
	"sv": "SE", // Swedish
	"da": "DK", // Danish
	"no": "NO", // Norwegian
	"fi": "FI", // Finnish
	"cs": "CZ", // Czech
	"el": "GR", // Greek
	"he": "IL", // Hebrew
	"ro": "RO", // Romanian
	"hu": "HU", // Hungarian
	"sk": "SK", // Slovak
}

// GetLanguageCountryCode returns the country code for a locale string (for flag display)
func GetLanguageCountryCode(localeStr string) string {
	if localeStr == "" {
		return ""
	}

	// Ensure locale cache is loaded
	if !localeCache.loaded {
		LoadLocales()
	}

	// Try to find exact locale match first (for codes like "en-US", "es-419", "ru-BY")
	_, region := locale.FindByLocale(localeStr, localeCache.entries)
	if region != nil && region.CountryCode != "" {
		return region.CountryCode
	}

	// For single-part locales (e.g., "en", "ja", "ar"), find language and use default country code
	parts := strings.Split(localeStr, "-")
	if len(parts) == 1 {
		langCode := strings.ToLower(parts[0])

		// Check default country codes first
		if countryCode, ok := defaultCountryCodes[langCode]; ok {
			return countryCode
		}

		// Fallback to first region's country code from locale data
		entry := locale.FindByLanguageCode(langCode, localeCache.entries)
		if entry != nil && len(entry.Regions) > 0 {
			return entry.Regions[0].CountryCode
		}
	}

	// Fallback: return empty string (will show UN flag)
	return ""
}

// GetRegionalLanguageName returns "Language - Region" or just "Language" for display
func GetRegionalLanguageName(localeStr string) string {
	if localeStr == "" {
		return ""
	}

	// Ensure locale cache is loaded
	if !localeCache.loaded {
		LoadLocales()
	}

	// Try to find exact locale match first
	entry, region := locale.FindByLocale(localeStr, localeCache.entries)
	if entry != nil && region != nil {
		// Regional locale: return "Language - Region"
		if region.CountryName != "" {
			// Check if language name already contains region name in parentheses
			// e.g., "Spanish (Latin America)" already contains "Latin America"
			regionInParens := fmt.Sprintf("(%s)", region.CountryName)
			if strings.Contains(entry.LanguageName, regionInParens) {
				// Language name already includes region, return as-is
				return entry.LanguageName
			}
			return fmt.Sprintf("%s - %s", entry.LanguageName, region.CountryName)
		}
	}

	// Single-part locale or no region match: just return language name
	parts := strings.Split(localeStr, "-")
	if len(parts) >= 1 {
		langCode := strings.ToLower(parts[0])
		entry = locale.FindByLanguageCode(langCode, localeCache.entries)
		if entry != nil {
			return entry.LanguageName
		}
	}

	// Fallback: return locale string as-is
	return localeStr
}

// GetRegionalNativeName returns "NativeLanguage - NativeRegion" or just "NativeLanguage" for display
func GetRegionalNativeName(localeStr string) string {
	if localeStr == "" {
		return ""
	}

	// Ensure locale cache is loaded
	if !localeCache.loaded {
		LoadLocales()
	}

	// Try to find exact locale match first
	entry, region := locale.FindByLocale(localeStr, localeCache.entries)
	if entry != nil && region != nil {
		// Regional locale: return "NativeLanguage - NativeRegion"
		if region.CountryLocal != "" {
			// Check if native language name already contains region name in parentheses
			// e.g., "Español (América Latina)" already contains "América Latina"
			regionInParens := fmt.Sprintf("(%s)", region.CountryLocal)
			if strings.Contains(entry.LanguageLocal, regionInParens) {
				// Native language name already includes region, return as-is
				return entry.LanguageLocal
			}
			// Only append region if different from language name
			if entry.LanguageLocal != region.CountryLocal {
				return fmt.Sprintf("%s - %s", entry.LanguageLocal, region.CountryLocal)
			}
		}
	}

	// Single-part locale or no region match: just return native language name
	parts := strings.Split(localeStr, "-")
	if len(parts) >= 1 {
		langCode := strings.ToLower(parts[0])
		entry = locale.FindByLanguageCode(langCode, localeCache.entries)
		if entry != nil {
			return entry.LanguageLocal
		}
	}

	// Fallback: return locale string as-is
	return localeStr
}

func FormatLanguageDisplay(localeStr string) string {
	// Return "Not set" for empty strings
	if localeStr == "" {
		return "Not set"
	}

	parts := strings.Split(localeStr, "-")
	langCode := strings.ToLower(parts[0])

	langEntry := locale.FindByLanguageCode(langCode, localeCache.entries)
	if langEntry == nil {
		return localeStr
	}

	// Handle single-part locale (e.g., "en") - Major/General language
	if len(parts) == 1 {
		flag := locale.GetFlagForLanguageCode(langCode)
		// Return native name only (e.g., "Português" not "Português - Portugal")
		return fmt.Sprintf("%s - %s", flag, langEntry.LanguageLocal)
	}

	// Handle two-part locale (e.g., "en-US") - Regional variant
	_, region := locale.FindByLocale(localeStr, localeCache.entries)
	if region == nil {
		flag := locale.GetFlagForLanguageCode(langCode)
		// Fallback for missing region data
		return fmt.Sprintf("%s - %s", flag, langEntry.LanguageLocal)
	}

	// For regional variants, use native region name if available
	displayName := region.CountryLocal
	if displayName == langEntry.LanguageName && region.CountryLocal != region.CountryName {
		// If native region name equals language name (e.g., "Français" = "Français"),
		// use country name instead (e.g., "Canada")
		displayName = region.CountryName
	}

	// Return native name only (e.g., "Português - Brasil" not "Português - Brazil")
	flag := GetFlagDisplay(localeStr)
	return fmt.Sprintf("%s - %s", flag, displayName)
}

func GetCommonLanguages() ([]locale.LocaleEntry, []string) {
	entries, err := LoadLocales()
	if err != nil {
		return []locale.LocaleEntry{}, []string{}
	}

	commonCodes := []string{
		"en", "ja", "es", "fr", "de", "zh", "ko", "it", "pt", "ru",
	}

	var result []locale.LocaleEntry
	var locales []string
	seen := make(map[string]bool)

	for _, code := range commonCodes {
		langEntry := locale.FindByLanguageCode(code, entries)
		if langEntry != nil && !seen[code] {
			result = append(result, *langEntry)
			seen[code] = true

			if len(langEntry.Regions) > 0 {
				locales = append(locales, langEntry.Regions[0].Locale)
			}
		}
	}

	return result, locales
}

func GetAllLanguages() ([]locale.LocaleEntry, []string) {
	entries, err := LoadLocales()
	if err != nil {
		return []locale.LocaleEntry{}, []string{}
	}

	sort.Slice(entries, func(i, j int) bool {
		return entries[i].LanguageName < entries[j].LanguageName
	})

	var locales []string
	for _, entry := range entries {
		if len(entry.Regions) > 0 {
			locales = append(locales, entry.Regions[0].Locale)
		}
	}

	return entries, locales
}

// For more advanced locale utilities, see locale_utils.go which provides:
// - LanguageOption struct with comprehensive display data
// - SearchLanguages() with flexible filtering (common only, search by term, limit)
// - GetCommonLanguageOptions() and GetAllLanguageOptions() returning LanguageOption
// - GetLanguagesByCountry() to get languages for a specific country
// - FindLanguageByLocale() to find a specific language option
// - GetFlagImageURL() to get flag image URL paths for web UI
// - FormatLanguageDisplayEntry() and FormatLanguageDisplayBasic() for formatted display
