// Client-side i18n system (WASM only)
// This module provides reactive translation functionality for client-side
// hydration

use std::collections::HashMap;

use leptos::prelude::*;
use rustelo_core_lib::{load_texts_from_ftl, Texts};
use serde::{Deserialize, Serialize};
use tracing::{debug, error, warn};
// wasm_bindgen import removed - not needed for basic types

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Language {
    pub code: String,
    pub display_name: String,
    pub is_default: bool,
}

impl Language {
    pub fn code(&self) -> &str {
        &self.code
    }

    pub fn display_name(&self) -> &str {
        &self.display_name
    }

    pub fn from_code(code: &str) -> Self {
        let language_registry = rustelo_core_lib::i18n::language_config::get_language_registry();
        let available_languages = language_registry.get_available_languages();
        let default_language = language_registry.get_default_language();

        // Debug logging for language registry
        web_sys::console::log_1(
            &format!(
                "🔍 Language registry - looking for '{}', available: {:?}, default: '{}'",
                code, available_languages, default_language
            )
            .into(),
        );

        // Find the language in the registry
        if let Some(lang_info) = language_registry.get_language(code) {
            web_sys::console::log_1(
                &format!(
                    "✅ Found language config for '{}': {}",
                    code, lang_info.native_name
                )
                .into(),
            );
            Language {
                code: code.to_string(),
                display_name: lang_info.native_name.clone(),
                is_default: code == default_language,
            }
        } else {
            // Fallback to default language
            web_sys::console::log_1(
                &format!(
                    "❌ Language '{}' not found in registry, falling back to default '{}'",
                    code, default_language
                )
                .into(),
            );
            if let Some(default_info) = language_registry.get_language(default_language) {
                Language {
                    code: default_language.to_string(),
                    display_name: default_info.native_name.clone(),
                    is_default: true,
                }
            } else {
                // Ultimate fallback
                Language {
                    code: "en".to_string(),
                    display_name: "English".to_string(),
                    is_default: true,
                }
            }
        }
    }

    pub fn all() -> Vec<Language> {
        let language_registry = rustelo_core_lib::i18n::language_config::get_language_registry();
        let _available_languages = language_registry.get_available_languages();
        let default_language = language_registry.get_default_language();

        _available_languages
            .iter()
            .map(|lang_code| {
                if let Some(lang_info) = language_registry.get_language(lang_code) {
                    Language {
                        code: lang_code.to_string(),
                        display_name: lang_info.native_name.clone(),
                        is_default: lang_code == default_language,
                    }
                } else {
                    Language {
                        code: lang_code.to_string(),
                        display_name: lang_code.to_string(),
                        is_default: lang_code == default_language,
                    }
                }
            })
            .collect()
    }
}

impl Default for Language {
    fn default() -> Self {
        let language_registry = rustelo_core_lib::i18n::language_config::get_language_registry();
        let default_language = language_registry.get_default_language();
        Language::from_code(default_language)
    }
}

#[derive(Clone, Copy)]
pub struct I18nContext {
    pub language: ReadSignal<Language>,
    pub set_language: WriteSignal<Language>,
    pub texts: Memo<Texts>,
    pub hydration_synced: ReadSignal<bool>,
}

impl I18nContext {
    /// Get translated text (non-reactive version)
    pub fn t(&self, key: &str, _args: Option<&HashMap<&str, &str>>) -> String {
        // Use get_untracked to avoid reactivity tracking in non-reactive contexts
        let texts = self.texts.get_untracked();
        let binding = self.language.get_untracked();
        let lang_code = binding.code();

        let translations = texts.get_translations_for_language(lang_code);

        let result = translations.get(key).cloned().unwrap_or_else(|| {
            if translations.is_empty() {
                error!(
                    "No translations loaded at all for language: '{}'",
                    lang_code
                );
            } else {
                warn!(
                    "Missing translation key: '{}' for language: '{}' (total keys loaded: {})",
                    key,
                    lang_code,
                    translations.len()
                );
            }

            // Return the key as fallback, prefixed to indicate missing translation
            format!("[{key}]")
        });

        result
    }

    /// Get translated text (reactive version for use in memos)
    pub fn t_reactive_memo(&self, key: &str) -> String {
        // Use reactive get() to track language and text changes
        let texts = self.texts.get();
        let binding = self.language.get();
        let lang_code = binding.code();

        let translations = texts.get_translations_for_language(lang_code);

        translations.get(key).cloned().unwrap_or_else(|| {
            warn!(
                "Missing translation key: '{}' for language: '{}'",
                key, lang_code
            );

            // Return the key as fallback, prefixed to indicate missing translation
            format!("[{key}]")
        })
    }

    /// Get translated text (reactive version) - returns a reactive closure
    pub fn t_reactive(&self, key: &'static str) -> impl Fn() -> String + Clone {
        let texts = self.texts;
        let language = self.language;
        move || {
            let texts = texts.get();
            let binding = language.get();
            let lang_code = binding.code();

            let translations = texts.get_translations_for_language(lang_code);

            translations.get(key).cloned().unwrap_or_else(|| {
                warn!(
                    "Missing translation key: '{}' for language: '{}'",
                    key, lang_code
                );

                // Return the key as fallback, prefixed to indicate missing translation
                format!("[{key}]")
            })
        }
    }

    /// Get translated text as a reactive signal - proper reactive solution
    pub fn t_signal(&self, key: &str) -> String {
        // Simplified: just return the current translation without memo
        self.t(key, None)
    }

    /// Get current language code (non-reactive)
    pub fn current_lang(&self) -> String {
        self.language.get_untracked().code().to_string()
    }

    /// Get current language code (reactive) - returns a Signal for proper
    /// reactivity
    pub fn current_lang_reactive(&self) -> Memo<String> {
        let language = self.language;
        Memo::new(move |_| language.get().code().to_string())
    }

    /// Check if current language is specific language (non-reactive)
    pub fn is_language(&self, lang: Language) -> bool {
        self.language.get_untracked() == lang
    }

    /// Check if current language is specific language (reactive)
    pub fn is_language_reactive(&self, lang: Language) -> Memo<bool> {
        let language = self.language;
        Memo::new(move |_| language.get() == lang)
    }
}

/// Helper functions for cookie-based language persistence
pub fn get_stored_language() -> Option<Language> {
    // Use JavaScript evaluation to read cookies since web_sys Document API is
    // limited
    match js_sys::eval("document.cookie") {
        Ok(cookie_value) => {
            if let Some(cookie_string) = cookie_value.as_string() {
                // Parse cookies to find preferred_language
                for cookie in cookie_string.split(';') {
                    let cookie = cookie.trim();
                    if let Some(value) = cookie.strip_prefix("preferred_language=") {
                        return Some(Language::from_code(value));
                    }
                }
            }
        }
        Err(_) => {
            web_sys::console::log_1(&"Failed to read cookies".into());
        }
    }
    None
}

fn store_language(language: &Language) {
    if let Some(_window) = web_sys::window() {
        // Use only cookies for consistent SSR/client behavior
        // Set cookie with 1 year expiration
        let cookie_script = format!(
            "document.cookie = 'preferred_language={}; path=/; max-age={}; SameSite=Lax'",
            language.code(),
            365 * 24 * 60 * 60 // 1 year in seconds
        );
        let _ = js_sys::eval(&cookie_script);
    }
}

#[component]
pub fn I18nProvider(children: leptos::prelude::Children) -> impl IntoView {
    // Initialize language with priority: cookies > URL detection > default
    let initial_language = {
        if let Some(window) = web_sys::window() {
            // PRIORITY 1: Check cookies first (user preference, works for SSR)
            if let Some(stored_lang) = get_stored_language() {
                web_sys::console::log_1(
                    &format!(
                        "🌐 Using cookie language preference: '{}'",
                        stored_lang.display_name()
                    )
                    .into(),
                );
                stored_lang
            } else if let Ok(pathname) = window.location().pathname() {
                // PRIORITY 2: Use URL-based detection if no cookie preference
                let (detected_lang, _) = crate::utils::extract_lang_and_path(&pathname);
                let detected_language = Language::from_code(&detected_lang);
                web_sys::console::log_1(
                    &format!(
                        "🌐 No cookie preference, detected language '{}' ({}) from URL: {}",
                        detected_lang,
                        detected_language.display_name(),
                        pathname
                    )
                    .into(),
                );
                detected_language
            } else {
                // PRIORITY 3: Default fallback
                web_sys::console::log_1(
                    &"🌐 No cookie preference or URL detection, defaulting to English".into(),
                );
                Language::default()
            }
        } else {
            Language::default()
        }
    };

    // Debug the initial language setting
    web_sys::console::log_1(
        &format!("🌐 I18nProvider initialized with language: {initial_language:?}").into(),
    );

    let (language, set_language) = signal(initial_language);

    // Track if we've synced the language after hydration
    let (hydration_synced, set_hydration_synced) = signal(false); // Start as not synced, will sync after DOM ready

    // Mark as hydrated after DOM is ready
    {
        gloo_timers::callback::Timeout::new(200, move || {
            web_sys::console::log_1(
                &"🌐 I18n hydration complete, enabling reactive NavMenu".into(),
            );
            set_hydration_synced.set(true);
        })
        .forget();
    }

    // Server-side language detection and redirect handles initial language
    // preference via cookies Client-side language sync happens only during SPA
    // navigation through LanguageSelector No automatic URL-based language
    // detection to avoid overriding user preferences

    // Load texts from FTL files - reactive to language changes, with fallback to
    // emergency hardcoded translations
    let texts = Memo::new(move |_| {
        // Make this reactive to language changes
        let current_lang = language.get(); // Reactive on client
        let lang_code = current_lang.code();

        match load_texts_from_ftl(lang_code) {
            Ok(texts) => {
                debug!(
                    "Successfully loaded translations for language {}: {} total languages with \
                     keys: {:?}",
                    lang_code,
                    texts.translations.len(),
                    texts
                        .translations
                        .iter()
                        .map(|(lang, keys)| format!("{}: {}", lang, keys.len()))
                        .collect::<Vec<_>>()
                        .join(", ")
                );

                texts
            }
            Err(e) => {
                error!(
                    "Failed to load texts from FTL for language {}: {}, returning empty \
                     translations",
                    lang_code, e
                );
                Texts::default()
            }
        }
    });

    let context = I18nContext {
        language,
        set_language,
        texts,
        hydration_synced,
    };

    provide_context(context);

    view! {
      {children()}
    }
}

#[derive(Clone, Copy)]
pub struct UseI18n(pub I18nContext);

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

impl UseI18n {
    pub fn new() -> Self {
        // During SSR, provide a minimal fallback context to avoid panics
        #[cfg(not(target_arch = "wasm32"))]
        {
            // Try to get context first, if available
            if let Some(context) = use_context::<I18nContext>() {
                return Self(context);
            }

            // Fallback for SSR when no context is provided
            let (language, set_language) = signal(Language::default());
            let (hydration_synced, _) = signal(true);

            // Use static texts without Memo to avoid reactive context issues during SSR
            let static_texts = load_texts_from_ftl("en").unwrap_or_default();
            let (texts_signal, _) = signal(static_texts);
            let texts = Memo::new(move |_| texts_signal.get());

            let fallback_context = I18nContext {
                language,
                set_language,
                texts,
                hydration_synced,
            };
            Self(fallback_context)
        }

        // On client-side, use the real context
        #[cfg(target_arch = "wasm32")]
        {
            // Try to get context first - if it fails, we need to provide a fallback
            if let Some(context) = use_context::<I18nContext>() {
                // Debug when components access i18n context
                // web_sys::console::log_1(
                //     &format!(
                //         "🌐 Component accessing i18n context, current language: {:?}",
                //         context.language.get_untracked()
                //     )
                //     .into(),
                // );
                Self(context)
            } else {
                // Create a minimal fallback context without memos
                web_sys::console::warn_1(
                    &"⚠️ No I18nProvider found, creating fallback context".into(),
                );

                let (language, set_language) = signal(Language::default());
                let (hydration_synced, _) = signal(false);

                // Use static texts without complex reactive logic for fallback
                let static_texts = load_texts_from_ftl("en").unwrap_or_default();
                let (texts_signal, _) = signal(static_texts);
                let texts = Memo::new(move |_| texts_signal.get());

                let fallback_context = I18nContext {
                    language,
                    set_language,
                    texts,
                    hydration_synced,
                };
                Self(fallback_context)
            }
        }
    }

    /// Create a localized URL based on current language and path
    pub fn localized_url(&self, path: &str) -> String {
        let lang_code = self.lang_code();
        if lang_code == "es" && !path.starts_with("/es") {
            if path == "/" {
                "/".to_string()
            } else {
                format!("/es{path}")
            }
        } else if lang_code == "en" && path.starts_with("/es") {
            if path == "/es" {
                "/".to_string()
            } else {
                path.strip_prefix("/es").unwrap_or("/").to_string()
            }
        } else {
            path.to_string()
        }
    }

    /// Get clean path without language prefix
    pub fn clean_path(&self, path: &str) -> String {
        if path.starts_with("/es/") {
            path.strip_prefix("/es").unwrap_or("/").to_string()
        } else if path == "/es" {
            "/".to_string()
        } else {
            path.to_string()
        }
    }

    /// Get translated text
    pub fn t(&self, key: &str) -> String {
        self.0.t(key, None)
    }

    /// Get translated text with arguments
    pub fn t_with_args(&self, key: &str, args: &HashMap<&str, &str>) -> String {
        self.0.t(key, Some(args))
    }

    /// Get translated text (reactive version) - returns a reactive closure
    pub fn t_reactive(&self, key: &'static str) -> impl Fn() -> String + Clone {
        self.0.t_reactive(key)
    }

    /// Get translated text as a reactive signal - proper reactive solution
    pub fn t_signal(&self, key: &str) -> String {
        self.0.t_signal(key)
    }

    /// Get translated text for use in reactive contexts (moves)
    /// Usage: {move || i18n.tr("key")}
    pub fn tr(&self, key: &str) -> String {
        let texts = self.0.texts.get_untracked();
        let binding = self.0.language.get_untracked();
        let lang_code = binding.code();

        let translations = texts.get_translations_for_language(lang_code);

        translations.get(key).cloned().unwrap_or_else(|| {
            warn!(
                "Missing translation key: '{}' for language: '{}'",
                key, lang_code
            );
            // Return the key as fallback, prefixed to indicate missing translation
            format!("[{key}]")
        })
    }

    /// Change language (with user preference tracking)
    pub fn set_language(&self, language: Language) {
        // Store user preference immediately
        store_language(&language);
        self.0.set_language.set(language);
    }

    /// Get current language
    pub fn language(&self) -> Language {
        self.0.language.get_untracked()
    }

    /// Check if current language is specific language (reactive)
    pub fn is_language_reactive(&self, lang: Language) -> Memo<bool> {
        self.0.is_language_reactive(lang)
    }

    /// Get current language code (non-reactive)
    pub fn lang_code(&self) -> String {
        self.0.current_lang()
    }

    /// Get current language code (reactive)
    pub fn lang_code_reactive(&self) -> Memo<String> {
        self.0.current_lang_reactive()
    }

    /// Build reactive page content patterns (client-side only)
    /// This is a reactive version of
    /// rustelo_core_lib::i18n::build_page_content_patterns that properly
    /// tracks language changes for NavMenu, Footer, and other components
    pub fn build_reactive_page_content_patterns(
        &self,
        patterns: &[&str],
    ) -> std::collections::HashMap<String, String> {
        // Use reactive methods to track language changes
        let texts = self.0.texts.get();
        let binding = self.0.language.get();
        let lang_code = binding.code();

        let translations = texts.get_translations_for_language(lang_code);

        // Discover keys matching patterns (same logic as shared version)
        let mut discovered_keys: Vec<String> = Vec::new();

        for pattern in patterns {
            let pattern_keys: Vec<String> = translations
                .keys()
                .filter(|key| key.starts_with(pattern))
                .cloned()
                .collect();

            discovered_keys.extend(pattern_keys);
        }

        // Remove duplicates while preserving order
        discovered_keys.sort();
        discovered_keys.dedup();

        // Build content HashMap with discovered keys using reactive translation
        discovered_keys
            .iter()
            .map(|key| (key.clone(), self.0.t_reactive_memo(key)))
            .collect()
    }

    /// Check if current language is specific language (non-reactive)
    pub fn is_language(&self, lang: Language) -> bool {
        self.0.is_language(lang)
    }
}

/// Hook to use internationalization
pub fn use_i18n() -> UseI18n {
    UseI18n::new()
}

// DEPRECATED: Language selector components have been moved to dedicated files.
// Please use: crate::components::language_selector::{LanguageSelector,
// LanguageToggle} Those components are language-agnostic and use
// ZERO-MAINTENANCE pattern-based approach.

// Emergency translations system removed - proper FTL loading should always
// work. If translations fail to load, components will show [key] format
// indicating missing keys.

#[cfg(test)]
mod tests {
    #[cfg(target_arch = "wasm32")]
    use super::*;

    #[test]
    #[cfg(target_arch = "wasm32")]
    fn test_language_codes() {
        assert_eq!(Language::default().code(), "en");
    }

    #[test]
    #[cfg(target_arch = "wasm32")]
    fn test_language_from_code() {
        assert_eq!(Language::from_code("en"), Language::default());
        assert_eq!(Language::from_code("invalid"), Language::default()); // Default fallback
    }

    #[test]
    #[cfg(target_arch = "wasm32")]
    fn test_language_display_names() {
        assert_eq!(Language::default().display_name(), "English");
    }
}
