// Unified i18n system that works identically in both SSR and client contexts
// Now fully language-agnostic using dynamic language discovery
use std::collections::HashMap;

#[cfg(any(target_arch = "wasm32", feature = "leptos-hydration"))]
use leptos::prelude::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

use crate::fluent::content_loader::load_texts_from_ftl;
use crate::i18n::page_translator::PageTranslator;
use crate::Texts;

/// Unified i18n struct that provides consistent translation functionality
/// in both SSR and client environments
#[derive(Clone, Debug, PartialEq)]
pub struct UnifiedI18n {
    pub language: String,
    pub translations: Texts,
}

impl UnifiedI18n {
    /// Create new UnifiedI18n instance with pre-loaded translations
    /// This works synchronously in both SSR and client contexts
    pub fn new(language: &str, _initial_path: &str) -> Self {
        let translations = load_translations_sync(language);
        Self {
            language: language.to_string(),
            translations,
        }
    }

    /// Get translation for a key in the current language
    pub fn t(&self, key: &str) -> String {
        let translations = self
            .translations
            .get_translations_for_language(&self.language);

        translations.get(key).cloned().unwrap_or_else(|| {
            // Return key in brackets to indicate missing translation
            format!("[{key}]")
        })
    }

    /// Get current language code
    pub fn lang_code(&self) -> &str {
        &self.language
    }

    /// Create localized URL based on current language - now language-agnostic
    pub fn localized_url(&self, path: &str) -> String {
        // Use the language-agnostic URL localizer
        crate::i18n::urls::localize_url(path, &self.language)
    }

    /// Check if current language is not the default language
    pub fn is_non_default_language(&self) -> bool {
        self.language != crate::i18n::discovery::get_default_language()
    }

    /// Check if this language is supported by the system
    pub fn is_supported_language(&self) -> bool {
        crate::i18n::discovery::is_supported_language(&self.language)
    }
}

/// Implementation of PageTranslator trait for UnifiedI18n
impl PageTranslator for UnifiedI18n {
    fn t(&self, key: &str) -> String {
        self.t(key)
    }

    fn localized_url(&self, path: &str) -> String {
        self.localized_url(path)
    }

    fn lang_code(&self) -> String {
        self.lang_code().to_string()
    }
}

/// Conditional console log for development - always enabled in debug builds
#[cfg(target_arch = "wasm32")]
macro_rules! debug_console_log {
    ($($arg:tt)*) => {
        #[cfg(debug_assertions)]
        {
            web_sys::console::log_1(&format!($($arg)*).into());
        }
    };
}

/// Load translations synchronously for both SSR and client
/// - On SSR: Loads from FTL files
/// - On WASM client: Uses embedded data from HTML or fetches from HTTP endpoint
fn load_translations_sync(language: &str) -> Texts {
    #[cfg(target_arch = "wasm32")]
    debug_console_log!(
        "🔄 load_translations_sync: Loading for language '{}'",
        language
    );

    // WASM: single-language cache loaded from embedded HTML once
    #[cfg(target_arch = "wasm32")]
    {
        thread_local! {
            static CACHED_ALL_TEXTS: std::cell::RefCell<Option<Texts>> = const { std::cell::RefCell::new(None) };
        }

        return CACHED_ALL_TEXTS.with(|cache| {
            let mut cache_ref = cache.borrow_mut();

            if cache_ref.is_none() {
                debug_console_log!(
                    "🔄 load_translations_sync: Loading from embedded HTML data or HTTP \
                     endpoint..."
                );

                if let Some(all_texts) = try_load_from_embedded_html() {
                    debug_console_log!(
                        "✅ load_translations_sync: Loaded from embedded HTML with {} languages",
                        all_texts.translations.len()
                    );
                    *cache_ref = Some(all_texts);
                } else {
                    debug_console_log!("⚠️ load_translations_sync: No embedded i18n data found");
                }
            }

            // Try HTML cache — direct lookup only, no fallback to other languages.
            // get_translations_for_language() has an or_else fallback that returns
            // any available language when the requested one is absent; that would
            // silently return EN data for an ES request, breaking language switching.
            if let Some(ref all_texts) = *cache_ref {
                if let Some(lang_keys) = all_texts.translations.get(language).cloned() {
                    if !lang_keys.is_empty() {
                        debug_console_log!(
                            "✅ load_translations_sync: {} keys for '{}' from HTML cache",
                            lang_keys.len(),
                            language
                        );
                        return crate::defs::Texts::from_language_maps(vec![(
                            language.to_string(),
                            lang_keys,
                        )]);
                    }
                }
            }

            // Fallback: FTL_REGISTRY populated by populate_wasm_ftl_registry() at startup.
            // This covers language-switch requests where the HTML only embedded the initial
            // language.
            if let Some(lang_map) = crate::registration::get_parsed_ftl_for_language(language) {
                if !lang_map.is_empty() {
                    debug_console_log!(
                        "✅ load_translations_sync: {} keys for '{}' from FTL_REGISTRY",
                        lang_map.len(),
                        language
                    );
                    // Cache the result so future calls for this language are fast.
                    let texts = crate::defs::Texts::from_language_maps(vec![(
                        language.to_string(),
                        lang_map,
                    )]);
                    if let Some(ref mut all_texts) = *cache_ref {
                        all_texts.translations.insert(
                            language.to_string(),
                            texts
                                .translations
                                .get(language)
                                .cloned()
                                .unwrap_or_default(),
                        );
                    }
                    return texts;
                }
            }

            crate::defs::Texts::default()
        });
    }

    // SSR: per-language cache keyed by language code.
    // FTL_REGISTRY is pre-populated at startup so each language loads once per
    // thread.
    #[cfg(not(target_arch = "wasm32"))]
    {
        use std::collections::HashMap;
        thread_local! {
            static CACHED_BY_LANG: std::cell::RefCell<HashMap<String, Texts>> =
                std::cell::RefCell::new(HashMap::new());
        }

        CACHED_BY_LANG.with(|cache| {
            let mut cache_ref = cache.borrow_mut();

            if !cache_ref.contains_key(language) {
                tracing::debug!(
                    "🔄 load_translations_sync: Loading FTL for language '{}'",
                    language
                );

                let lang_map = crate::registration::get_parsed_ftl_for_language(language)
                    .filter(|m| !m.is_empty())
                    .or_else(|| {
                        load_texts_from_ftl("").ok().and_then(|texts| {
                            let m = texts.get_translations_for_language(language);
                            if m.is_empty() {
                                None
                            } else {
                                Some(m)
                            }
                        })
                    })
                    .unwrap_or_default();

                tracing::debug!(
                    "✅ load_translations_sync: Loaded {} keys for language '{}'",
                    lang_map.len(),
                    language
                );

                cache_ref.insert(
                    language.to_string(),
                    crate::defs::Texts::from_language_maps(vec![(language.to_string(), lang_map)]),
                );
            }

            cache_ref.get(language).cloned().unwrap_or_default()
        })
    }
}

/// Try to load translations from embedded HTML data (populated by server in
/// shell.rs)
#[cfg(target_arch = "wasm32")]
fn try_load_from_embedded_html() -> Option<Texts> {
    use web_sys::window;

    // Find the i18n-data script element that contains current language translations
    if let Some(win) = window() {
        if let Some(doc) = win.document() {
            if let Some(elem) = doc.get_element_by_id("i18n-data") {
                let text = elem.text_content().unwrap_or_default();
                if !text.is_empty() {
                    // Try to parse the embedded JSON data
                    if let Ok(lang_keys) = serde_json::from_str::<HashMap<String, String>>(&text) {
                        // Get the language from the data-lang attribute
                        if let Some(lang) = elem.get_attribute("data-lang") {
                            debug_console_log!(
                                "✅ try_load_from_embedded_html: Found {} keys for language '{}'",
                                lang_keys.len(),
                                lang
                            );

                            // Create a minimal Texts object with just this language
                            return Some(crate::defs::Texts::from_language_maps(vec![(
                                lang, lang_keys,
                            )]));
                        }
                    }
                }
            }
        }
    }

    debug_console_log!("⚠️ try_load_from_embedded_html: Could not find embedded i18n data");
    None
}

/// Detect language from URL path using dynamic language discovery and route
/// configurations
pub fn detect_language_from_path(path: &str) -> String {
    let discovered_languages = crate::i18n::discovery::discover_available_languages();

    // Check if path starts with any discovered language prefix
    if let Some(lang_part) = path.strip_prefix('/').and_then(|p| p.split('/').next()) {
        if discovered_languages.contains(&lang_part.to_string()) {
            // Check if this is a valid language in our routing config
            if !crate::routing::config::get_routes_for_language(lang_part).is_empty() {
                return lang_part.to_string();
            }
        }
    }

    // Check if this path exists as a non-default route
    let config = crate::routing::config::get_routing_config();
    let default_lang = crate::i18n::discovery::get_default_language();

    for route in &config.routes {
        if route.enabled && route.path == path && route.language != default_lang {
            return route.language.clone();
        }
    }

    // Default to discovered default language
    default_lang.to_string()
}

/// Fetch parsed translations for `language` from the server's i18n API.
///
/// Only available in WASM builds.  The server endpoint (`GET /api/i18n/{lang}`)
/// reads from `FTL_REGISTRY`, which is populated by the implementation's
/// `ResourceContributor::contribute_ftl()`.
#[cfg(target_arch = "wasm32")]
pub async fn fetch_translations(
    language: &str,
) -> Result<std::collections::HashMap<String, String>, String> {
    let url = format!("/api/i18n/{}", language);
    reqwasm::http::Request::get(&url)
        .send()
        .await
        .map_err(|e| format!("fetch /api/i18n/{}: {}", language, e))?
        .json::<std::collections::HashMap<String, String>>()
        .await
        .map_err(|e| format!("parse /api/i18n/{} response: {}", language, e))
}

/// Context key for UnifiedI18n
#[derive(Clone, Copy)]
pub struct UnifiedI18nContext;

/// Hook to use unified i18n in Leptos components (SSR and client).
#[cfg(any(target_arch = "wasm32", feature = "leptos-hydration"))]
pub fn use_unified_i18n() -> UnifiedI18n {
    if let Some(i18n) = use_context::<UnifiedI18n>() {
        return i18n;
    }
    UnifiedI18n::new(crate::i18n::discovery::get_default_language(), "/")
}

/// Provide unified i18n context for Leptos child components.
#[cfg(any(target_arch = "wasm32", feature = "leptos-hydration"))]
pub fn provide_unified_i18n(i18n: UnifiedI18n) {
    provide_context(i18n);
}

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

    #[test]
    fn test_unified_i18n_creation() {
        let i18n = UnifiedI18n::new("en", "/");
        assert_eq!(i18n.lang_code(), "en");
        assert!(i18n.is_supported_language());
    }

    #[test]
    fn test_spanish_i18n() {
        let i18n = UnifiedI18n::new("es", "/es");
        assert_eq!(i18n.lang_code(), "es");
        assert!(i18n.is_supported_language());
        assert!(!i18n.is_non_default_language() || i18n.is_non_default_language());
        // Either can be true depending on system config
    }

    #[test]
    fn test_localized_urls() {
        let en_i18n = UnifiedI18n::new("en", "/");
        assert_eq!(en_i18n.localized_url("/"), "/");
        assert_eq!(en_i18n.localized_url("/contact"), "/contact");

        let es_i18n = UnifiedI18n::new("es", "/es");
        assert_eq!(es_i18n.localized_url("/"), "/es");
        assert_eq!(es_i18n.localized_url("/contact"), "/es/contactar");
    }

    #[test]
    fn test_translations() {
        let i18n = UnifiedI18n::new("en", "/");
        let title = i18n.t("not-found-title");

        // Should either get the FTL translation or the emergency fallback
        assert!(title == "Page Not Found" || title.starts_with("["));
    }

    #[test]
    fn test_language_detection() {
        let default_lang = crate::i18n::discovery::get_default_language();

        assert_eq!(detect_language_from_path("/"), default_lang);
        assert_eq!(detect_language_from_path("/about"), default_lang);

        // Test with discovered languages
        let discovered = crate::i18n::discovery::discover_available_languages();
        for lang in discovered {
            if lang != default_lang {
                let path = format!("/{}", lang);
                let detected = detect_language_from_path(&path);
                // Should detect the language if it's in routing config
                assert!(detected == *lang || detected == default_lang);
            }
        }
    }
}
