//! Locale resolution with priority-based fallback

use crate::config::TypeDialogConfig;
use unic_langid::LanguageIdentifier;

/// Resolver for determining which locale to use based on multiple sources
pub struct LocaleResolver {
    config: TypeDialogConfig,
}

impl LocaleResolver {
    /// Create a new locale resolver with the given configuration
    pub fn new(config: TypeDialogConfig) -> Self {
        Self { config }
    }

    /// Resolve the locale to use based on priority
    ///
    /// Priority order:
    /// 1. CLI flag `--locale`
    /// 2. Form TOML attribute `locale`
    /// 3. Global config `~/.config/typedialog/config.toml`
    /// 4. Environment variable `TYPEDIALOG_LANG`
    /// 5. Environment variables `LANG` or `LC_ALL`
    /// 6. Fallback to "en-US"
    pub fn resolve(
        &self,
        cli_locale: Option<&str>,
        form_locale: Option<&str>,
    ) -> LanguageIdentifier {
        // Priority 1: CLI flag
        if let Some(locale) = cli_locale {
            if let Ok(lang_id) = locale.parse() {
                return lang_id;
            }
        }

        // Priority 2: Form TOML attribute
        if let Some(locale) = form_locale {
            if let Ok(lang_id) = locale.parse() {
                return lang_id;
            }
        }

        // Priority 3: Global config
        if let Some(locale) = &self.config.locale {
            if let Ok(lang_id) = locale.parse() {
                return lang_id;
            }
        }

        // Priority 4: Environment variables
        if let Some(lang_id) = self.detect_system_locale() {
            return lang_id;
        }

        // Priority 5: Fallback
        self.config
            .fallback_locale
            .parse()
            .unwrap_or_else(|_| "en-US".parse().unwrap())
    }

    /// Detect system locale from environment variables
    /// Priority: TYPEDIALOG_LANG > LANG > LC_ALL
    fn detect_system_locale(&self) -> Option<LanguageIdentifier> {
        // Priority 1: TYPEDIALOG_LANG (explicit typedialog-specific setting)
        if let Ok(locale_str) = std::env::var("TYPEDIALOG_LANG") {
            if let Ok(lang_id) = locale_str.parse() {
                return Some(lang_id);
            }
        }

        // Priority 2: Try LANG, then LC_ALL (system locale)
        let locale_str = std::env::var("LANG")
            .or_else(|_| std::env::var("LC_ALL"))
            .ok()?;

        // Extract just the language part (e.g., "en_US.UTF-8" → "en-US")
        let parts: Vec<&str> = locale_str.split_whitespace().collect();
        let base = parts.first().unwrap_or(&"en-US");

        // Remove encoding (e.g., ".UTF-8")
        let lang_part = base.split('.').next().unwrap_or(base);

        // Convert underscore to hyphen (e.g., "en_US" → "en-US")
        let normalized = lang_part.replace('_', "-");

        normalized.parse().ok()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::TypeDialogConfig;

    #[test]
    fn test_locale_resolution_priority() {
        let config = TypeDialogConfig::default();
        let resolver = LocaleResolver::new(config);

        // CLI flag should have highest priority
        let locale = resolver.resolve(Some("es-ES"), Some("fr-FR"));
        assert_eq!(locale.to_string(), "es-ES");

        // Form locale if no CLI flag
        let locale = resolver.resolve(None, Some("fr-FR"));
        assert_eq!(locale.to_string(), "fr-FR");

        // Fallback if no CLI or form locale
        let locale = resolver.resolve(None, None);
        assert_eq!(locale.to_string(), "en-US");
    }

    #[test]
    fn test_invalid_locale_format() {
        let config = TypeDialogConfig::default();
        let resolver = LocaleResolver::new(config);

        // Invalid format should fall through to next priority
        let locale = resolver.resolve(Some("invalid!!!"), Some("fr-FR"));
        assert_eq!(locale.to_string(), "fr-FR");
    }

    #[test]
    fn test_system_locale_parsing() {
        let config = TypeDialogConfig::default();
        let resolver = LocaleResolver::new(config);

        // The actual system locale will vary, so just test parsing doesn't panic
        let _locale = resolver.detect_system_locale();
    }
}
