//! Dynamic Language Configuration System
//!
//! This module provides a dynamic language detection and configuration system
//! that automatically discovers available languages from FTL content files
//! and configures the application accordingly. Now integrated with the central
//! language discovery system for consistency.

use std::collections::{HashMap, HashSet};
use std::sync::OnceLock;

use serde::{Deserialize, Serialize};

/// Language configuration with metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageConfig {
    /// Language code (e.g., "en", "es", "fr")
    pub code: String,
    /// Native display name (e.g., "English", "Español", "Français")
    pub native_name: String,
    /// English display name (e.g., "English", "Spanish", "French")
    pub english_name: String,
    /// Flag emoji or icon identifier
    pub flag: Option<String>,
    /// Whether this is the default/fallback language
    pub is_default: bool,
    /// Language direction (ltr, rtl)
    pub direction: TextDirection,
    /// Optional locale for date/number formatting
    pub locale: Option<String>,
}

/// Text direction for language layout
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub enum TextDirection {
    #[default]
    LeftToRight,
    RightToLeft,
}

/// Global language configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageRegistry {
    /// Available languages in the application
    pub languages: HashMap<String, LanguageConfig>,
    /// Default language code
    pub default_language: String,
    /// Ordered list of language codes by priority
    pub language_priority: Vec<String>,
}

impl LanguageRegistry {
    /// Create a new language registry with automatic detection using central
    /// discovery system
    pub fn new() -> Self {
        let mut registry = Self::with_minimal_defaults();

        // Use central language discovery system for consistency
        let detected_languages = crate::discovery::discover_available_languages();
        registry.merge_detected_languages(detected_languages.clone());

        registry
    }

    /// Create registry with minimal defaults (fallback only if no languages
    /// discovered)
    fn with_minimal_defaults() -> Self {
        let default_lang = crate::discovery::get_default_language();
        let mut languages = HashMap::new();

        // Only create default language config as fallback
        if let Some(config) = create_language_config(default_lang) {
            languages.insert(default_lang.to_string(), config);
        }

        Self {
            languages,
            default_language: default_lang.to_string(),
            language_priority: vec![default_lang.to_string()],
        }
    }

    /// Merge detected languages with existing configuration
    fn merge_detected_languages(&mut self, detected: Vec<String>) {
        // Sort detected languages for consistent ordering
        let mut sorted_detected = detected.clone();
        sorted_detected.sort();

        // Add detected languages to configuration
        for lang_code in &sorted_detected {
            if !self.languages.contains_key(lang_code) {
                if let Some(config) = create_language_config(lang_code) {
                    self.languages.insert(lang_code.clone(), config);
                    if !self.language_priority.contains(lang_code) {
                        self.language_priority.push(lang_code.clone());
                    }
                }
            }
        }

        // Intelligent default language selection
        self.select_optimal_default_language(&sorted_detected);

        tracing::info!(
            "Language registry updated: {} languages available, default: {}",
            self.languages.len(),
            self.default_language
        );
        tracing::debug!("Language priority order: {:?}", self.language_priority);
    }

    /// Select the most appropriate default language based on content
    /// availability and preferences
    fn select_optimal_default_language(&mut self, detected: &[String]) {
        // Language preference order (common languages first)
        let preferred_languages = ["en", "es"];

        // If current default exists in detected languages and is valid, keep it
        if detected.contains(&self.default_language)
            && self.languages.contains_key(&self.default_language)
        {
            if let Some(config) = self.languages.get_mut(&self.default_language) {
                config.is_default = true;
            }
            return;
        }

        // Find the best default language from detected languages
        let mut new_default = None;

        // First, try to find a preferred language that was detected
        for &preferred in &preferred_languages {
            if detected.contains(&preferred.to_string()) {
                new_default = Some(preferred.to_string());
                break;
            }
        }

        // If no preferred language found, use the first detected language
        if new_default.is_none() && !detected.is_empty() {
            new_default = Some(detected[0].clone());
        }

        // If still no default and we have any languages, use the first available
        if new_default.is_none() && !self.languages.is_empty() {
            new_default = Some(self.language_priority[0].clone());
        }

        // Apply the new default language
        if let Some(new_lang) = new_default {
            // Remove default flag from current default
            if let Some(current_default) = self.languages.get_mut(&self.default_language) {
                current_default.is_default = false;
            }

            // Set new default
            self.default_language = new_lang.clone();
            if let Some(new_default_config) = self.languages.get_mut(&new_lang) {
                new_default_config.is_default = true;
            }

            // Move default language to front of priority list
            if let Some(pos) = self.language_priority.iter().position(|x| x == &new_lang) {
                let lang = self.language_priority.remove(pos);
                self.language_priority.insert(0, lang);
            }

            tracing::info!("Default language set to: {}", self.default_language);
        }
    }

    /// Get all available language codes
    pub fn get_available_languages(&self) -> Vec<String> {
        self.language_priority.clone()
    }

    /// Get language configuration by code
    pub fn get_language(&self, code: &str) -> Option<&LanguageConfig> {
        self.languages.get(code)
    }

    /// Get default language code
    pub fn get_default_language(&self) -> &str {
        &self.default_language
    }

    /// Check if a language is supported
    pub fn is_language_supported(&self, code: &str) -> bool {
        self.languages.contains_key(code)
    }

    /// Get display name for a language code
    pub fn get_display_name(&self, code: &str, in_english: bool) -> String {
        if let Some(config) = self.languages.get(code) {
            if in_english {
                config.english_name.clone()
            } else {
                config.native_name.clone()
            }
        } else {
            code.to_uppercase()
        }
    }

    /// Get flag emoji for a language
    pub fn get_flag(&self, code: &str) -> Option<String> {
        self.languages.get(code)?.flag.clone()
    }

    /// Get text direction for a language
    pub fn get_text_direction(&self, code: &str) -> TextDirection {
        self.languages
            .get(code)
            .map(|config| config.direction.clone())
            .unwrap_or_default()
    }

    /// Get all languages with their display information
    pub fn get_all_with_display(&self, in_english: bool) -> Vec<(String, String, Option<String>)> {
        self.language_priority
            .iter()
            .filter_map(|code| {
                let config = self.languages.get(code)?;
                let name = if in_english {
                    &config.english_name
                } else {
                    &config.native_name
                };
                Some((code.clone(), name.clone(), config.flag.clone()))
            })
            .collect()
    }

    /// Set default language (useful for configuration)
    pub fn set_default_language(&mut self, code: &str) -> Result<(), String> {
        if !self.languages.contains_key(code) {
            return Err(format!("Language '{code}' is not available"));
        }

        // Remove default flag from current default
        if let Some(current_default) = self.languages.get_mut(&self.default_language) {
            current_default.is_default = false;
        }

        // Set new default
        self.default_language = code.to_string();
        if let Some(new_default) = self.languages.get_mut(code) {
            new_default.is_default = true;
        }

        // Move default language to front of priority list
        if let Some(pos) = self.language_priority.iter().position(|x| x == code) {
            let lang = self.language_priority.remove(pos);
            self.language_priority.insert(0, lang);
        }

        tracing::info!(
            "Default language manually set to: {}",
            self.default_language
        );
        Ok(())
    }

    /// Reload language configuration by re-detecting content files
    pub fn reload(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        tracing::info!("Reloading language registry from content files...");

        // Use central discovery system
        let detected_languages = crate::discovery::discover_available_languages().clone();

        // Clear existing non-default languages that weren't detected
        let mut languages_to_remove = Vec::new();
        for (code, config) in &self.languages {
            if !config.is_default && !detected_languages.contains(code) {
                languages_to_remove.push(code.clone());
            }
        }

        for code in languages_to_remove {
            self.languages.remove(&code);
            self.language_priority.retain(|x| x != &code);
            tracing::debug!(
                "Removed language '{}' - no longer detected in content",
                code
            );
        }

        // Merge newly detected languages
        self.merge_detected_languages(detected_languages);

        Ok(())
    }

    /// Check if language configuration is up to date with content files
    pub fn is_up_to_date(&self) -> bool {
        // Use central discovery system
        let detected = crate::discovery::discover_available_languages().clone();

        // Check if all detected languages are in our registry
        for lang in &detected {
            if !self.languages.contains_key(lang) {
                return false;
            }
        }

        // Check if we have languages that are no longer detected
        let available_codes: std::collections::HashSet<_> = self.languages.keys().collect();
        let detected_set: std::collections::HashSet<_> = detected.iter().collect();

        // If we have languages not in detected set (excluding default), we're outdated
        let default_lang = crate::discovery::get_default_language();
        for code in &available_codes {
            if *code != default_lang && !detected_set.contains(code) {
                return false;
            }
        }

        true
    }

    /// Get language statistics
    pub fn get_statistics(&self) -> std::collections::HashMap<String, String> {
        let mut stats = std::collections::HashMap::new();
        stats.insert(
            "total_languages".to_string(),
            self.languages.len().to_string(),
        );
        stats.insert(
            "default_language".to_string(),
            self.default_language.clone(),
        );
        stats.insert(
            "available_languages".to_string(),
            self.language_priority.join(", "),
        );

        // Count languages by text direction
        let ltr_count = self
            .languages
            .values()
            .filter(|config| config.direction == TextDirection::LeftToRight)
            .count();
        let rtl_count = self
            .languages
            .values()
            .filter(|config| config.direction == TextDirection::RightToLeft)
            .count();

        stats.insert("ltr_languages".to_string(), ltr_count.to_string());
        stats.insert("rtl_languages".to_string(), rtl_count.to_string());

        stats
    }
}

impl Default for LanguageRegistry {
    fn default() -> Self {
        Self::new()
    }
}

/// Global registry instance using OnceCell for thread-safe initialization
static GLOBAL_LANGUAGE_REGISTRY: OnceLock<LanguageRegistry> = OnceLock::new();

/// Get the global language registry instance (singleton)
pub fn get_language_registry() -> &'static LanguageRegistry {
    GLOBAL_LANGUAGE_REGISTRY.get_or_init(|| {
        tracing::info!("Initializing global language registry...");
        LanguageRegistry::new()
    })
}

/// Initialize the global language registry (explicit initialization)
pub fn initialize_language_registry() -> &'static LanguageRegistry {
    get_language_registry()
}

/// Simple language code validation function
fn is_valid_language_code(code: &str) -> bool {
    // Basic validation: 2-3 letter codes, optionally with region (en-US)
    if code.is_empty() || code.len() < 2 {
        return false;
    }

    // Check format: lowercase letters, optionally followed by dash and uppercase
    // region
    let parts: Vec<&str> = code.split('-').collect();
    if parts.len() > 2 {
        return false;
    }

    // Language part should be 2-3 lowercase letters
    let lang_part = parts[0];
    if lang_part.len() < 2
        || lang_part.len() > 3
        || !lang_part.chars().all(|c| c.is_ascii_lowercase())
    {
        return false;
    }

    // Region part (if present) should be 2 uppercase letters
    if parts.len() == 2 {
        let region_part = parts[1];
        if region_part.len() != 2 || !region_part.chars().all(|c| c.is_ascii_uppercase()) {
            return false;
        }
    }

    true
}

/// Create language configuration for a language code using only dynamic content
/// loading
fn create_language_config(code: &str) -> Option<LanguageConfig> {
    // Get display names from FTL translations only
    let (native_name, english_name) = get_language_display_names_from_ftl(code);

    let config = LanguageConfig {
        code: code.to_string(),
        native_name,
        english_name,
        flag: None, // No hardcoded flags - can be added via FTL if needed
        is_default: false,
        direction: TextDirection::LeftToRight, // Default, can be overridden via FTL
        locale: None,                          /* No hardcoded locales - can be added via FTL if
                                                * needed */
    };

    Some(config)
}

/// Get language display names - simplified version without fluent dependency
///
/// Note: This is a simplified implementation for the extracted crate.
/// Full FTL-based display name loading can be added later through a trait-based
/// abstraction.
fn get_language_display_names_from_ftl(code: &str) -> (String, String) {
    // Minimal implementation - use uppercase language code
    // This can be enhanced later by passing display names through configuration
    // or by implementing a trait that rustelo_core_lib can provide
    (code.to_uppercase(), code.to_uppercase())
}

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

    #[test]
    fn test_language_code_validation() {
        assert!(is_valid_language_code("en"));
        assert!(is_valid_language_code("es"));
        assert!(!is_valid_language_code(""));
        assert!(!is_valid_language_code("e"));
        assert!(!is_valid_language_code("EN"));
        assert!(!is_valid_language_code("english"));
        assert!(!is_valid_language_code("en-us"));
    }

    #[test]
    fn test_language_registry_creation() {
        let registry = LanguageRegistry::with_minimal_defaults();
        let default_lang = crate::discovery::get_default_language();
        assert!(registry.is_language_supported(default_lang));
        assert_eq!(registry.get_default_language(), default_lang);
        assert!(!registry.get_available_languages().is_empty());
    }

    #[test]
    fn test_language_config_creation() {
        let config = create_language_config("es").unwrap();
        assert_eq!(config.code, "es");
        // Native name uses uppercase code in simplified implementation
        // Full FTL-based display names can be added through trait-based abstraction
        assert_eq!(config.native_name, "ES");
        // English name also uses uppercase code in simplified implementation
        assert_eq!(config.english_name, "ES");
        assert_eq!(config.flag, None); // No hardcoded flags
    }

    #[test]
    fn test_text_direction() {
        let config_ltr = create_language_config("en").unwrap();
        assert_eq!(config_ltr.direction, TextDirection::LeftToRight);

        // Arabic defaults to LTR since there's no specific FTL configuration for
        // direction
        let config_ar = create_language_config("ar").unwrap();
        assert_eq!(config_ar.direction, TextDirection::LeftToRight);
    }

    #[test]
    fn test_language_merging() {
        let mut registry = LanguageRegistry::with_minimal_defaults();
        let _initial_count = registry.get_available_languages().len(); // Depends on discovery

        // Simulate detecting additional languages
        let detected = vec!["es".to_string(), "fr".to_string()];
        registry.merge_detected_languages(detected);

        // Should have at least the detected languages
        assert!(registry.is_language_supported("es"));
        assert!(registry.is_language_supported("fr"));
        assert!(registry.get_available_languages().len() >= 2);
    }

    #[test]
    fn test_default_language_selection() {
        let mut registry = LanguageRegistry::with_minimal_defaults();

        // Simulate detecting multiple languages
        let detected = vec!["de".to_string(), "es".to_string(), "fr".to_string()];
        registry.merge_detected_languages(detected);

        // Spanish should be selected as default (it's in preferred list and comes
        // before others)
        assert_eq!(registry.get_default_language(), "es");

        // Test with no preferred languages
        let mut registry2 = LanguageRegistry::with_minimal_defaults();
        let detected2 = vec!["sw".to_string(), "zu".to_string()]; // Non-preferred languages
        registry2.merge_detected_languages(detected2);

        // Should pick the first detected language
        assert_eq!(registry2.get_default_language(), "sw");
    }

    #[test]
    fn test_language_priority_ordering() {
        let mut registry = LanguageRegistry::with_minimal_defaults();
        let detected = vec!["de".to_string(), "es".to_string(), "fr".to_string()];
        registry.merge_detected_languages(detected);

        let priority = registry.get_available_languages();

        // Default language should be first
        assert_eq!(priority[0], registry.get_default_language());

        // Languages should be sorted appropriately
        assert!(priority.contains(&"de".to_string()));
        assert!(priority.contains(&"es".to_string()));
        assert!(priority.contains(&"fr".to_string()));
    }

    #[test]
    fn test_language_statistics() {
        let registry = LanguageRegistry::with_minimal_defaults();
        let stats = registry.get_statistics();

        assert!(stats.contains_key("total_languages"));
        assert!(stats.contains_key("default_language"));
        assert!(stats.contains_key("available_languages"));
        assert!(stats.contains_key("ltr_languages"));
        assert!(stats.contains_key("rtl_languages"));

        // Should have at least one language
        let total: usize = stats.get("total_languages").unwrap().parse().unwrap();
        assert!(total >= 1);

        let default_lang = crate::discovery::get_default_language();
        assert_eq!(stats.get("default_language").unwrap(), default_lang);
    }

    #[test]
    fn test_manual_default_change() {
        let mut registry = LanguageRegistry::with_minimal_defaults();
        let detected = vec!["es".to_string(), "fr".to_string()];
        registry.merge_detected_languages(detected);

        // Change default to French
        let result = registry.set_default_language("fr");
        assert!(result.is_ok());
        assert_eq!(registry.get_default_language(), "fr");

        // Verify French is marked as default
        let fr_config = registry.get_language("fr").unwrap();
        assert!(fr_config.is_default);

        // Verify priority order updated
        let priority = registry.get_available_languages();
        assert_eq!(priority[0], "fr");
    }

    #[test]
    fn test_invalid_default_language() {
        let mut registry = LanguageRegistry::with_minimal_defaults();
        let original_default = registry.get_default_language().to_string();
        let result = registry.set_default_language("nonexistent");
        assert!(result.is_err());
        assert_eq!(registry.get_default_language(), &original_default); // Should remain unchanged
    }
}
