use leptos::prelude::*;
use serde::{Deserialize, Serialize};
use shared::{Texts, load_texts_toml};
use std::collections::HashMap;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::spawn_local;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Language {
    English,
    Spanish,
}

impl Language {
    pub fn code(&self) -> &'static str {
        match self {
            Language::English => "en",
            Language::Spanish => "es",
        }
    }

    pub fn display_name(&self) -> &'static str {
        match self {
            Language::English => "English",
            Language::Spanish => "Español",
        }
    }

    pub fn from_code(code: &str) -> Self {
        match code {
            "es" => Language::Spanish,
            _ => Language::English, // Default to English
        }
    }

    pub fn all() -> Vec<Language> {
        vec![Language::English, Language::Spanish]
    }
}

impl Default for Language {
    fn default() -> Self {
        Language::English
    }
}

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

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 lang_code = self.language.get_untracked().code();

        let translations = match lang_code {
            "es" => &texts.es,
            _ => &texts.en,
        };

        translations
            .get(key)
            .cloned()
            .unwrap_or_else(|| key.to_string())
    }

    /// 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 lang_code = language.get().code();

            let translations = match lang_code {
                "es" => &texts.es,
                _ => &texts.en,
            };

            translations
                .get(key)
                .cloned()
                .unwrap_or_else(|| key.to_string())
        }
    }

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

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

#[component]
pub fn I18nProvider(children: leptos::prelude::Children) -> impl IntoView {
    // Initialize language from localStorage or default to English
    let initial_language = Language::default();

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

    // Load texts from embedded resources
    let texts = Memo::new(move |_| load_texts_toml().unwrap_or_default());

    let context = I18nContext {
        language: language.into(),
        set_language,
        texts,
    };

    provide_context(context);

    view! {
        {children()}
    }
}

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

impl UseI18n {
    pub fn new() -> Self {
        Self(expect_context::<I18nContext>())
    }

    /// 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)
    }

    /// Change language
    pub fn set_language(&self, language: Language) {
        self.0.set_language.set(language);
    }

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

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

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

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

/// Language selector component
#[component]
pub fn LanguageSelector(#[prop(optional)] class: Option<String>) -> impl IntoView {
    let i18n = use_i18n();
    let (is_open, set_is_open) = signal(false);

    view! {
        <div class=move || format!(
            "relative inline-block text-left {}",
            class.as_deref().unwrap_or("")
        )>
            <button
                type="button"
                class="inline-flex items-center justify-center px-2 py-1 text-sm font-medium bg-white dark:bg-gray-800 text-stone-800 dark:text-gray-200 border border-stone-200 dark:border-gray-600 rounded-lg hover:bg-stone-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all duration-200"
                on:click=move |_| set_is_open.update(|open| *open = !*open)
                aria-expanded=move || is_open.get_untracked()
                aria-haspopup="true"
            >
                <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"/>
                </svg>
                {
                    let i18n_clone = i18n.clone();
                    move || i18n_clone.0.language.get_untracked().code().to_uppercase()
                }
                <svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
                </svg>
            </button>

            <Show when=move || is_open.get_untracked()>
                <div class="absolute right-0 top-full z-[9999] w-40 mt-1 origin-top-right bg-white dark:bg-gray-800 border border-stone-200 dark:border-gray-600 rounded-lg shadow-xl ring-1 ring-stone-950 dark:ring-gray-700 ring-opacity-5 focus:outline-none">
                    <div class="py-1" role="menu" aria-orientation="vertical">
                        {
                            let i18n_clone = i18n.clone();
                            let languages = Language::all();
                            languages.into_iter().map(|lang| {
                                let i18n_item = i18n_clone.clone();
                                let lang_for_click = lang.clone();
                                let i18n_for_click = i18n_item.clone();
                                let lang_for_reactive = lang.clone();
                                let i18n_for_reactive = i18n_item.clone();
                                let lang_for_show1 = lang.clone();
                                let i18n_for_show1 = i18n_item.clone();
                                let lang_for_show2 = lang.clone();
                                let i18n_for_show2 = i18n_item.clone();
                                let lang_for_display = lang.clone();

                                view! {
                                    <button
                                        type="button"
                                        class=move || format!(
                                            "flex items-center w-full px-4 py-2 text-sm text-left hover:bg-stone-50 dark:hover:bg-gray-700 focus:outline-none focus:bg-stone-50 dark:focus:bg-gray-700 transition-colors duration-200 {}",
                                            if i18n_for_reactive.is_language(lang_for_reactive.clone()) { "bg-blue-50 dark:bg-blue-900 text-blue-700 dark:text-blue-300 font-medium" } else { "text-stone-700 dark:text-gray-300 hover:text-stone-900 dark:hover:text-gray-100" }
                                        )
                                        role="menuitem"
                                        on:click=move |_| {
                                            i18n_for_click.set_language(lang_for_click.clone());
                                            set_is_open.set(false);
                                        }
                                    >
                                        <Show when=move || i18n_for_show1.is_language(lang_for_show1.clone())>
                                            <svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
                                                <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
                                            </svg>
                                        </Show>
                                        <Show when=move || !i18n_for_show2.is_language(lang_for_show2.clone())>
                                            <div class="w-4 h-4 mr-2"></div>
                                        </Show>
                                        {lang_for_display.display_name()}
                                    </button>
                                }.into_any()
                            }).collect::<Vec<_>>()
                        }
                    </div>
                </div>
            </Show>

            // Click outside to close
            <Show when=move || is_open.get_untracked()>
                <div
                    class="fixed inset-0 z-40"
                    on:click=move |_| set_is_open.set(false)
                ></div>
            </Show>
        </div>
    }
}

/// Compact language toggle component
#[component]
pub fn LanguageToggle(#[prop(optional)] class: Option<String>) -> impl IntoView {
    let i18n = use_i18n();

    view! {
        <button
            type="button"
            class=move || format!(
                "inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 {}",
                class.as_deref().unwrap_or("")
            )
            on:click={
                let i18n_clone = i18n.clone();
                move |_| {
                    let current = i18n_clone.0.language.get_untracked();
                    let new_lang = match current {
                        Language::English => Language::Spanish,
                        Language::Spanish => Language::English,
                    };
                    i18n_clone.set_language(new_lang);
                }
            }
            title={
                let i18n_clone = i18n.clone();
                move || i18n_clone.t("select-language")
            }
        >
            <svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"/>
            </svg>
            {
                let i18n_clone = i18n.clone();
                move || i18n_clone.0.language.get_untracked().code().to_uppercase()
            }
        </button>
    }
}

// Dark Mode Context and Components
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Theme {
    Light,
    Dark,
}

impl Theme {
    pub fn to_class(&self) -> &'static str {
        match self {
            Theme::Light => "light",
            Theme::Dark => "dark",
        }
    }

    pub fn is_dark(&self) -> bool {
        matches!(self, Theme::Dark)
    }
}

impl Default for Theme {
    fn default() -> Self {
        Theme::Light
    }
}

#[derive(Clone)]
pub struct ThemeContext {
    pub theme: ReadSignal<Theme>,
    pub set_theme: WriteSignal<Theme>,
}

impl ThemeContext {
    pub fn new() -> Self {
        // Default to light theme on server-side
        let initial_theme = Theme::Light;
        let (theme, set_theme) = signal(initial_theme);

        // Only run client-side code after hydration
        #[cfg(target_arch = "wasm32")]
        {
            // Initialize theme from localStorage on client
            spawn_local(async move {
                if let Some(window) = web_sys::window() {
                    if let Ok(Some(storage)) = window.local_storage() {
                        if let Ok(Some(stored_theme)) = storage.get_item("theme") {
                            let saved_theme = match stored_theme.as_str() {
                                "dark" => Theme::Dark,
                                _ => Theme::Light,
                            };
                            set_theme.set(saved_theme);
                        }
                    }
                }
            });

            // Save theme to localStorage and update document class when it changes
            // Only create effect if window exists (client-side)
            if web_sys::window().is_some() {
                Effect::new(move |_| {
                    let current_theme = theme.get();
                    if let Some(window) = web_sys::window() {
                        if let Ok(Some(storage)) = window.local_storage() {
                            let theme_str = match current_theme {
                                Theme::Light => "light",
                                Theme::Dark => "dark",
                            };
                            let _ = storage.set_item("theme", theme_str);
                        }

                        // Update document class for dark mode
                        if let Some(document) = window.document() {
                            if let Some(html) = document.document_element() {
                                match current_theme {
                                    Theme::Dark => {
                                        let _ = html.class_list().add_1("dark");
                                    }
                                    Theme::Light => {
                                        let _ = html.class_list().remove_1("dark");
                                    }
                                }
                            }
                        }
                    }
                });
            }
        }

        Self { theme, set_theme }
    }

    pub fn toggle_theme(&self) {
        let new_theme = match self.theme.get_untracked() {
            Theme::Light => Theme::Dark,
            Theme::Dark => Theme::Light,
        };
        self.set_theme.set(new_theme);
    }

    pub fn is_dark(&self) -> bool {
        self.theme.get_untracked().is_dark()
    }
}

// Theme context provider
#[component]
pub fn ThemeProvider(children: Children) -> impl IntoView {
    // Only create theme context on client-side to avoid SSR issues
    #[cfg(target_arch = "wasm32")]
    {
        let theme_context = ThemeContext::new();
        provide_context(theme_context);
    }

    // On server-side, provide a minimal theme context
    #[cfg(not(target_arch = "wasm32"))]
    {
        let (theme, set_theme) = signal(Theme::Light);
        let theme_context = ThemeContext { theme, set_theme };
        provide_context(theme_context);
    }

    children()
}

// Theme hook
pub fn use_theme() -> ThemeContext {
    expect_context::<ThemeContext>()
}

// Dark mode toggle component
#[component]
pub fn DarkModeToggle(#[prop(optional)] class: Option<String>) -> impl IntoView {
    let theme_context = use_theme();
    let theme_context_click = theme_context.clone();
    let theme_context_title = theme_context.clone();
    let theme_context_sun = theme_context.clone();
    let theme_context_moon = theme_context.clone();

    view! {
        <button
            type="button"
            class=move || format!(
                "inline-flex items-center justify-center p-2 text-sm font-medium bg-white dark:bg-stone-800 text-stone-800 dark:text-stone-200 border border-stone-200 dark:border-stone-700 rounded-lg hover:bg-stone-50 dark:hover:bg-stone-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all duration-200 {}",
                class.as_deref().unwrap_or("")
            )
            on:click=move |_| theme_context_click.toggle_theme()
            title=move || if theme_context_title.theme.get_untracked().is_dark() { "Switch to light mode" } else { "Switch to dark mode" }
        >
            <Show when=move || theme_context_sun.theme.get_untracked().is_dark()>
                // Sun icon for light mode
                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/>
                </svg>
            </Show>
            <Show when=move || !theme_context_moon.theme.get_untracked().is_dark()>
                // Moon icon for dark mode
                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
                </svg>
            </Show>
        </button>
    }
}

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

    #[test]
    fn test_language_codes() {
        assert_eq!(Language::English.code(), "en");
        assert_eq!(Language::Spanish.code(), "es");
    }

    #[test]
    fn test_language_from_code() {
        assert_eq!(Language::from_code("en"), Language::English);
        assert_eq!(Language::from_code("es"), Language::Spanish);
        assert_eq!(Language::from_code("invalid"), Language::English); // Default fallback
    }

    #[test]
    fn test_language_display_names() {
        assert_eq!(Language::English.display_name(), "English");
        assert_eq!(Language::Spanish.display_name(), "Español");
    }
}
