use std::collections::HashMap;

use gloo_storage::{LocalStorage, Storage};
use leptos::prelude::*;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{window, Document, Element, HtmlElement};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum Theme {
    Light,
    Dark,
    Auto, // System preference
    Custom(String),
}

impl Theme {
    pub fn as_string(&self) -> String {
        match self {
            Theme::Light => "light".to_string(),
            Theme::Dark => "dark".to_string(),
            Theme::Auto => "auto".to_string(),
            Theme::Custom(name) => name.clone(),
        }
    }

    pub fn from_string(s: &str) -> Self {
        match s {
            "light" => Theme::Light,
            "dark" => Theme::Dark,
            "auto" => Theme::Auto,
            custom => Theme::Custom(custom.to_string()),
        }
    }

    pub fn display_name(&self) -> &str {
        match self {
            Theme::Light => "Light",
            Theme::Dark => "Dark",
            Theme::Auto => "Auto",
            Theme::Custom(name) => name,
        }
    }

    pub fn icon(&self) -> &str {
        match self {
            Theme::Light => "bi-sun",
            Theme::Dark => "bi-moon",
            Theme::Auto => "bi-circle-half",
            Theme::Custom(_) => "bi-palette",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThemeConfig {
    pub current_theme: Theme,
    pub available_themes: Vec<Theme>,
    pub custom_properties: HashMap<String, String>,
    pub auto_switch_enabled: bool,
    pub system_preference_override: Option<Theme>,
}

impl Default for ThemeConfig {
    fn default() -> Self {
        Self {
            current_theme: Theme::Auto,
            available_themes: vec![Theme::Light, Theme::Dark, Theme::Auto],
            custom_properties: HashMap::new(),
            auto_switch_enabled: true,
            system_preference_override: None,
        }
    }
}

#[derive(Debug, Clone)]
pub struct ThemeContext {
    pub config: RwSignal<ThemeConfig>,
    pub resolved_theme: Memo<Theme>,
    pub is_dark_mode: Memo<bool>,
}

impl ThemeContext {
    pub fn new() -> Self {
        // Load theme from localStorage or use default
        let stored_config =
            LocalStorage::get::<ThemeConfig>("dashboard_theme_config").unwrap_or_default();

        let config = RwSignal::new(stored_config);

        // Create system theme preference detector
        let system_theme = create_signal_from_system_preference();

        // Resolve the actual theme to use
        let resolved_theme = Memo::new(move |_| {
            let current_config = config.get();
            match current_config.current_theme {
                Theme::Auto => {
                    if let Some(override_theme) = current_config.system_preference_override {
                        override_theme
                    } else {
                        system_theme.get()
                    }
                }
                theme => theme,
            }
        });

        let is_dark_mode = Memo::new(move |_| match resolved_theme.get() {
            Theme::Dark => true,
            Theme::Custom(ref name) if name.contains("dark") => true,
            _ => false,
        });

        // Save config to localStorage when it changes
        Effect::new(move |_| {
            let current_config = config.get();
            let _ = LocalStorage::set("dashboard_theme_config", &current_config);
        });

        // Apply theme to document
        Effect::new(move |_| {
            apply_theme_to_document(&resolved_theme.get(), &config.get().custom_properties);
        });

        Self {
            config,
            resolved_theme,
            is_dark_mode,
        }
    }

    pub fn set_theme(&self, theme: Theme) {
        self.config.update(|config| {
            config.current_theme = theme;
        });
    }

    pub fn toggle_theme(&self) {
        let current = self.resolved_theme.get();
        let new_theme = match current {
            Theme::Light => Theme::Dark,
            Theme::Dark => Theme::Light,
            Theme::Auto => {
                // Toggle based on current resolved theme
                if self.is_dark_mode.get() {
                    Theme::Light
                } else {
                    Theme::Dark
                }
            }
            Theme::Custom(_) => Theme::Light, // Default fallback
        };
        self.set_theme(new_theme);
    }

    pub fn add_custom_property(&self, property: String, value: String) {
        self.config.update(|config| {
            config.custom_properties.insert(property, value);
        });
    }

    pub fn remove_custom_property(&self, property: &str) {
        self.config.update(|config| {
            config.custom_properties.remove(property);
        });
    }

    pub fn reset_to_defaults(&self) {
        self.config.set(ThemeConfig::default());
    }

    pub fn get_css_variables(&self) -> HashMap<String, String> {
        let theme = self.resolved_theme.get();
        let config = self.config.get();
        let mut variables = get_theme_css_variables(&theme);

        // Add custom properties
        for (key, value) in config.custom_properties {
            variables.insert(key, value);
        }

        variables
    }
}

#[component]
pub fn ThemeProvider(
    #[prop(optional)] theme: Option<ReadSignal<String>>,
    children: Children,
) -> impl IntoView {
    // Create theme context
    let theme_context = ThemeContext::new();

    // Provide context to children first
    provide_context(theme_context.clone());

    // If a specific theme is provided via prop, use it
    if let Some(theme_signal) = theme {
        let theme_ctx = theme_context.clone();
        Effect::new(move |_| {
            let theme_str = theme_signal.get();
            if !theme_str.is_empty() {
                theme_ctx.set_theme(Theme::from_string(&theme_str));
            }
        });
    }

    // Apply theme classes to body
    let theme_ctx_for_effect = theme_context.clone();
    Effect::new(move |_| {
        if let Some(body) = document().body() {
            let theme = theme_ctx_for_effect.resolved_theme.get();
            let theme_class = format!("theme-{}", theme.as_string());

            // Remove existing theme classes
            let class_list = body.class_list();
            for i in 0..class_list.length() {
                if let Some(class_name) = class_list.item(i) {
                    if class_name.starts_with("theme-") {
                        let _ = class_list.remove_1(&class_name);
                    }
                }
            }

            // Add current theme class
            let _ = class_list.add_1(&theme_class);

            // Add dark mode class for convenience
            if theme_ctx_for_effect.is_dark_mode.get() {
                let _ = class_list.add_1("dark-mode");
            } else {
                let _ = class_list.remove_1("dark-mode");
            }
        }
    });

    view! {
        <div class="theme-provider">
            {children()}
        </div>
    }
}

#[component]
pub fn ThemeToggleButton(
    #[prop(optional)] size: Option<ButtonSize>,
    #[prop(optional)] variant: Option<ButtonVariant>,
    #[prop(optional)] show_label: Option<bool>,
) -> impl IntoView {
    let theme_context = use_theme_context();
    let size = size.unwrap_or(ButtonSize::Medium);
    let variant = variant.unwrap_or(ButtonVariant::Ghost);
    let show_label = show_label.unwrap_or(false);

    let button_class = move || {
        format!(
            "theme-toggle-btn btn-{} btn-{}",
            size.as_str(),
            variant.as_str()
        )
    };

    let current_theme = theme_context.resolved_theme;
    let is_dark = theme_context.is_dark_mode;

    let toggle_theme = move |_| {
        theme_context.toggle_theme();
    };

    view! {
        <button
            class=button_class
            on:click=toggle_theme
            title=move || format!("Switch to {} theme", if is_dark.get() { "light" } else { "dark" })
        >
            <i class=move || {
                let theme = current_theme.get();
                theme.icon().to_string()
            }></i>
            <Show when=move || show_label>
                <span class="btn-label">
                    {move || {
                        let theme = current_theme.get();
                        theme.display_name().to_string()
                    }}
                </span>
            </Show>
        </button>
    }
}

#[component]
pub fn ThemeSelector(#[prop(optional)] compact: Option<bool>) -> impl IntoView {
    let theme_context = use_theme_context();
    let compact = compact.unwrap_or(false);
    let (show_dropdown, set_show_dropdown) = signal(false);

    let config = theme_context.config;
    let current_theme = theme_context.resolved_theme;

    let toggle_dropdown = move |_| {
        set_show_dropdown.update(|show| *show = !*show);
    };

    view! {
        <div class="theme-selector" class:compact=compact>
            <button
                class="theme-selector-trigger"
                on:click=toggle_dropdown
            >
                <i class=move || {
                    let theme = current_theme.get();
                    theme.icon().to_string()
                }></i>
                <Show when=move || !compact>
                    <span class="theme-name">
                        {move || {
                            let theme = current_theme.get();
                            theme.display_name().to_string()
                        }}
                    </span>
                </Show>
                <i class="bi-chevron-down dropdown-icon"></i>
            </button>

            <Show when=move || show_dropdown.get()>
                <div class="theme-dropdown">
                    <For
                        each=move || config.get().available_themes
                        key=|theme| theme.as_string()
                        let:theme
                    >
                        {
                            let theme_ctx = use_theme_context();
                            let theme_clone = theme.clone();
                            let theme_clone2 = theme.clone();
                            let is_current = Memo::new(move |_| {
                                config.get().current_theme == theme_clone
                            });
                            let theme_icon = theme.icon().to_string();
                            let theme_label = theme.display_name().to_string();

                            view! {
                                <button
                                    class=move || format!("theme-option {}", if is_current.get() { "active" } else { "" })
                                    on:click=move |_| {
                                        theme_ctx.set_theme(theme_clone2.clone());
                                        set_show_dropdown.set(false);
                                    }
                                >
                                    <i class=theme_icon.clone()></i>
                                    <span class="theme-label">{theme_label.clone()}</span>
                                    <Show when=move || is_current.get()>
                                        <i class="bi-check theme-check"></i>
                                    </Show>
                                </button>
                            }
                        }
                    </For>

                    <div class="theme-dropdown-divider"></div>

                    <button class="theme-option" on:click=move |_| {
                        // Open theme customization
                        set_show_dropdown.set(false);
                    }>
                        <i class="bi-palette"></i>
                        <span class="theme-label">"Customize"</span>
                    </button>
                </div>
            </Show>
        </div>
    }
}

#[component]
pub fn ThemeCustomizer() -> impl IntoView {
    let theme_context = use_theme_context();
    let (is_open, set_is_open) = signal(false);

    let config = theme_context.config;
    let theme_ctx_clone = theme_context.clone();
    let css_variables = Memo::new(move |_| theme_ctx_clone.get_css_variables());

    // Color customization
    let (custom_primary, set_custom_primary) = signal("#007bff".to_string());
    let (custom_secondary, set_custom_secondary) = signal("#6c757d".to_string());
    let (custom_background, set_custom_background) = signal("#ffffff".to_string());

    view! {
        <div class="theme-customizer">
            <button
                class="customizer-trigger"
                on:click=move |_| set_is_open.update(|open| *open = !*open)
            >
                <i class="bi-palette"></i>
                "Customize Theme"
            </button>

            <Show when=move || is_open.get()>
                <div class="customizer-panel">
                    <div class="customizer-header">
                        <h3>"Theme Customization"</h3>
                        <button
                            class="close-btn"
                            on:click=move |_| set_is_open.set(false)
                        >
                            <i class="bi-x"></i>
                        </button>
                    </div>

                    <div class="customizer-content">
                        <div class="color-section">
                            <h4>"Colors"</h4>

                            <div class="color-input-group">
                                <label>"Primary Color"</label>
                                <input
                                    type="color"
                                    prop:value=custom_primary
                                    on:input=move |ev| set_custom_primary.set(event_target_value(&ev))
                                />
                            </div>

                            <div class="color-input-group">
                                <label>"Secondary Color"</label>
                                <input
                                    type="color"
                                    prop:value=custom_secondary
                                    on:input=move |ev| set_custom_secondary.set(event_target_value(&ev))
                                />
                            </div>

                            <div class="color-input-group">
                                <label>"Background Color"</label>
                                <input
                                    type="color"
                                    prop:value=custom_background
                                    on:input=move |ev| set_custom_background.set(event_target_value(&ev))
                                />
                            </div>
                        </div>

                        <div class="preview-section">
                            <h4>"Preview"</h4>
                            <div class="theme-preview">
                                <div class="preview-card">
                                    <div class="preview-header" style=move || format!("background-color: {}", custom_primary.get())>
                                        "Header"
                                    </div>
                                    <div class="preview-content" style=move || format!("background-color: {}", custom_background.get())>
                                        "Content area with custom colors"
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div class="customizer-actions">
                            <button
                                class="btn btn-primary"
                                on:click={
                                    let theme_ctx = use_theme_context();
                                    move |_| {
                                        theme_ctx.add_custom_property("--color-primary".to_string(), custom_primary.get());
                                        theme_ctx.add_custom_property("--color-secondary".to_string(), custom_secondary.get());
                                        theme_ctx.add_custom_property("--color-background".to_string(), custom_background.get());
                                    }
                                }
                            >
                                "Apply Changes"
                            </button>
                            <button
                                class="btn btn-secondary"
                                on:click={
                                    let theme_ctx = use_theme_context();
                                    move |_| {
                                        theme_ctx.config.update(|config| {
                                            config.custom_properties.clear();
                                        });
                                    }
                                }
                            >
                                "Reset to Default"
                            </button>
                        </div>

                        <div class="css-variables-section">
                            <h4>"CSS Variables"</h4>
                            <div class="variables-list">
                                {move || {
                                    css_variables.get().into_iter().map(|(var_name, var_value)| {
                                        view! {
                                            <div class="variable-item">
                                                <code class="variable-name">{var_name.clone()}</code>
                                                <code class="variable-value">{var_value.clone()}</code>
                                            </div>
                                        }
                                    }).collect_view()
                                }}
                            </div>
                        </div>
                    </div>
                </div>
            </Show>
        </div>
    }
}

// Supporting types and utilities

#[derive(Debug, Clone, Copy)]
pub enum ButtonSize {
    Small,
    Medium,
    Large,
}

impl ButtonSize {
    pub fn as_str(&self) -> &'static str {
        match self {
            ButtonSize::Small => "sm",
            ButtonSize::Medium => "md",
            ButtonSize::Large => "lg",
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum ButtonVariant {
    Primary,
    Secondary,
    Ghost,
    Outline,
}

impl ButtonVariant {
    pub fn as_str(&self) -> &'static str {
        match self {
            ButtonVariant::Primary => "primary",
            ButtonVariant::Secondary => "secondary",
            ButtonVariant::Ghost => "ghost",
            ButtonVariant::Outline => "outline",
        }
    }
}

// Utility functions

pub fn use_theme_context() -> ThemeContext {
    use_context::<ThemeContext>().expect("ThemeContext must be provided by ThemeProvider")
}

fn create_signal_from_system_preference() -> ReadSignal<Theme> {
    let (system_theme, set_system_theme) = signal(get_system_theme_preference());

    // Listen for system theme changes
    let window = window().unwrap();
    let media_query = window
        .match_media("(prefers-color-scheme: dark)")
        .unwrap()
        .unwrap();

    let callback = Closure::wrap(Box::new(move |_: web_sys::MediaQueryListEvent| {
        set_system_theme.set(get_system_theme_preference());
    }) as Box<dyn FnMut(web_sys::MediaQueryListEvent)>);

    media_query.set_onchange(Some(callback.as_ref().unchecked_ref()));
    callback.forget();

    system_theme
}

fn get_system_theme_preference() -> Theme {
    if let Some(window) = window() {
        if let Ok(Some(media_query)) = window.match_media("(prefers-color-scheme: dark)") {
            if media_query.matches() {
                return Theme::Dark;
            }
        }
    }
    Theme::Light
}

fn document() -> Document {
    window().unwrap().document().unwrap()
}

fn apply_theme_to_document(theme: &Theme, custom_properties: &HashMap<String, String>) {
    if let Some(document_element) = document().document_element() {
        if let Ok(html_element) = document_element.dyn_into::<HtmlElement>() {
            let style = html_element.style();

            // Apply base theme variables
            let theme_vars = get_theme_css_variables(theme);
            for (property, value) in theme_vars {
                let _ = style.set_property(&property, &value);
            }

            // Apply custom properties
            for (property, value) in custom_properties {
                let _ = style.set_property(property, value);
            }
        }
    }
}

fn get_theme_css_variables(theme: &Theme) -> HashMap<String, String> {
    let mut vars = HashMap::new();

    match theme {
        Theme::Light => {
            vars.insert("--color-primary".to_string(), "#007bff".to_string());
            vars.insert("--color-secondary".to_string(), "#6c757d".to_string());
            vars.insert("--color-success".to_string(), "#28a745".to_string());
            vars.insert("--color-warning".to_string(), "#ffc107".to_string());
            vars.insert("--color-danger".to_string(), "#dc3545".to_string());
            vars.insert("--color-info".to_string(), "#17a2b8".to_string());

            vars.insert("--bg-primary".to_string(), "#ffffff".to_string());
            vars.insert("--bg-secondary".to_string(), "#f8f9fa".to_string());
            vars.insert("--bg-tertiary".to_string(), "#e9ecef".to_string());

            vars.insert("--text-primary".to_string(), "#212529".to_string());
            vars.insert("--text-secondary".to_string(), "#6c757d".to_string());
            vars.insert("--text-muted".to_string(), "#6c757d".to_string());

            vars.insert("--border-color".to_string(), "#dee2e6".to_string());
            vars.insert("--border-color-light".to_string(), "#f1f3f4".to_string());

            vars.insert(
                "--shadow-sm".to_string(),
                "0 0.125rem 0.25rem rgba(0, 0, 0, 0.075)".to_string(),
            );
            vars.insert(
                "--shadow".to_string(),
                "0 0.5rem 1rem rgba(0, 0, 0, 0.15)".to_string(),
            );
            vars.insert(
                "--shadow-lg".to_string(),
                "0 1rem 3rem rgba(0, 0, 0, 0.175)".to_string(),
            );
        }
        Theme::Dark => {
            vars.insert("--color-primary".to_string(), "#0d6efd".to_string());
            vars.insert("--color-secondary".to_string(), "#6c757d".to_string());
            vars.insert("--color-success".to_string(), "#198754".to_string());
            vars.insert("--color-warning".to_string(), "#ffc107".to_string());
            vars.insert("--color-danger".to_string(), "#dc3545".to_string());
            vars.insert("--color-info".to_string(), "#0dcaf0".to_string());

            vars.insert("--bg-primary".to_string(), "#1a1a1a".to_string());
            vars.insert("--bg-secondary".to_string(), "#2d2d2d".to_string());
            vars.insert("--bg-tertiary".to_string(), "#404040".to_string());

            vars.insert("--text-primary".to_string(), "#ffffff".to_string());
            vars.insert("--text-secondary".to_string(), "#adb5bd".to_string());
            vars.insert("--text-muted".to_string(), "#6c757d".to_string());

            vars.insert("--border-color".to_string(), "#404040".to_string());
            vars.insert("--border-color-light".to_string(), "#2d2d2d".to_string());

            vars.insert(
                "--shadow-sm".to_string(),
                "0 0.125rem 0.25rem rgba(0, 0, 0, 0.3)".to_string(),
            );
            vars.insert(
                "--shadow".to_string(),
                "0 0.5rem 1rem rgba(0, 0, 0, 0.4)".to_string(),
            );
            vars.insert(
                "--shadow-lg".to_string(),
                "0 1rem 3rem rgba(0, 0, 0, 0.5)".to_string(),
            );
        }
        Theme::Auto => {
            // Auto theme will be resolved to Light or Dark
            vars.extend(get_theme_css_variables(&get_system_theme_preference()));
        }
        Theme::Custom(_) => {
            // Custom themes would be loaded from configuration
            vars.extend(get_theme_css_variables(&Theme::Light)); // Fallback
        }
    }

    vars
}
