use leptos::prelude::*;
use serde::{Deserialize, Serialize};

/// Theme variants supported by the application
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum Theme {
    #[default]
    Light,
    Dark,
    Auto,
}

impl Theme {
    /// Get the CSS class name for the theme
    pub fn as_class(&self) -> &'static str {
        match self {
            Theme::Light => "theme-light",
            Theme::Dark => "theme-dark",
            Theme::Auto => "theme-auto",
        }
    }

    /// Get the data attribute value for DaisyUI
    pub fn as_data_theme(&self) -> &'static str {
        match self {
            Theme::Light => "light",
            Theme::Dark => "dark",
            Theme::Auto => "light",
        }
    }

    /// Get all available themes
    pub fn all() -> Vec<Theme> {
        vec![Theme::Light, Theme::Dark, Theme::Auto]
    }

    /// Get display name for the theme
    pub fn display_name(&self) -> &'static str {
        match self {
            Theme::Light => "Light",
            Theme::Dark => "Dark",
            Theme::Auto => "Auto",
        }
    }

    /// Get icon for the theme
    pub fn icon(&self) -> &'static str {
        match self {
            Theme::Light => "i-carbon-sun",
            Theme::Dark => "i-carbon-moon",
            Theme::Auto => "i-carbon-settings",
        }
    }
}

/// Theme state management
#[derive(Debug, Clone)]
pub struct ThemeState {
    pub current_theme: RwSignal<Theme>,
    pub system_theme: RwSignal<Theme>,
}

impl Default for ThemeState {
    fn default() -> Self {
        Self {
            current_theme: RwSignal::new(Theme::Light),
            system_theme: RwSignal::new(Self::detect_system_theme()),
        }
    }
}

impl ThemeState {
    /// Create a new theme state with initial theme
    pub fn new(initial_theme: Theme) -> Self {
        Self {
            current_theme: RwSignal::new(initial_theme),
            system_theme: RwSignal::new(Self::detect_system_theme()),
        }
    }

    /// Detect system theme preference
    fn detect_system_theme() -> Theme {
        Theme::Light
    }

    /// Toggle between light and dark themes
    pub fn toggle(&self) {
        let current = self.current_theme.get_untracked();
        let new_theme = match current {
            Theme::Light => Theme::Dark,
            Theme::Dark => Theme::Light,
            Theme::Auto => Theme::Light,
        };
        self.set_theme(new_theme);
    }

    /// Set the current theme
    pub fn set_theme(&self, theme: Theme) {
        self.current_theme.set(theme);
        self.apply_theme(theme);
    }

    /// Apply theme to the DOM
    fn apply_theme(&self, _theme: Theme) {
        // Theme application would be handled by CSS/JavaScript
        // For now, we'll keep this simple
    }

    /// Get the effective theme (resolves Auto to Light/Dark)
    pub fn effective_theme(&self) -> Theme {
        match self.current_theme.get_untracked() {
            Theme::Auto => self.system_theme.get_untracked(),
            theme => theme,
        }
    }

    /// Initialize theme system with system preference detection
    pub fn init(&self) {
        // Apply initial theme
        self.apply_theme(self.current_theme.get_untracked());

        // Set up system theme change listener
        self.setup_system_theme_listener();
    }

    /// Set up listener for system theme changes
    fn setup_system_theme_listener(&self) {
        // System theme listening would be handled by JavaScript
        // For now, we'll keep this simple
    }
}

/// Theme provider component
#[component]
pub fn ThemeProvider(
    #[prop(optional)] initial_theme: Option<Theme>,
    children: leptos::children::Children,
) -> impl IntoView {
    let theme_state = ThemeState::new(initial_theme.unwrap_or_default());
    theme_state.init();

    provide_context(theme_state);

    view! {
      {children()}
    }
}

/// Hook to use theme state
pub fn use_theme_state() -> ThemeState {
    use_context::<ThemeState>()
        .expect("ThemeState context not found. Make sure ThemeProvider is set up.")
}

/// Theme toggle button component
#[component]
pub fn ThemeToggle(#[prop(optional)] class: Option<String>) -> impl IntoView {
    let theme_state = use_theme_state();
    let current_theme = theme_state.current_theme;

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

    view! {
      <button
        class=move || format!("btn ds-btn-ghost btn-circle {}", class.as_deref().unwrap_or(""))
        on:click=toggle_theme
        title=move || format!("Switch to {} theme",
          match current_theme.get_untracked() {
            Theme::Light => "dark",
            Theme::Dark => "light",
            Theme::Auto => "light",
          }
        )
      >
        <div class=move || format!("w-5 h-5 {}", current_theme.get_untracked().icon())></div>
      </button>
    }
}

/// Theme selector dropdown component
#[component]
pub fn ThemeSelector(#[prop(optional)] class: Option<String>) -> impl IntoView {
    let theme_state = use_theme_state();
    let current_theme = theme_state.current_theme;

    view! {
      <div class=move || format!("dropdown dropdown-end {}", class.as_deref().unwrap_or(""))>
        <div tabindex="0" role="button" class="btn ds-btn-ghost btn-circle">
          <div class=move || format!("w-5 h-5 {}", current_theme.get_untracked().icon())></div>
        </div>
        <ul tabindex="0" class="dropdown-content z-[1] menu p-ds-2 shadow bg-base-100 ds-rounded-box w-52">
          {Theme::all().into_iter().map(|theme| {
            let theme_state = theme_state.clone();
            let is_active = move || current_theme.get_untracked() == theme;

            view! {
              <li>
                <a
                  class=move || if is_active() { "active" } else { "" }
                  on:click=move |_| theme_state.set_theme(theme)
                >
                  <div class=format!("w-4 h-4 {}", theme.icon())></div>
                  {theme.display_name()}
                </a>
              </li>
            }
          }).collect::<Vec<_>>()}
        </ul>
      </div>
    }
}

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

    #[test]
    fn test_theme_display_names() {
        assert_eq!(Theme::Light.display_name(), "Light");
        assert_eq!(Theme::Dark.display_name(), "Dark");
        assert_eq!(Theme::Auto.display_name(), "Auto");
    }

    #[test]
    fn test_theme_data_attributes() {
        assert_eq!(Theme::Light.as_data_theme(), "light");
        assert_eq!(Theme::Dark.as_data_theme(), "dark");
    }

    #[test]
    fn test_theme_classes() {
        assert_eq!(Theme::Light.as_class(), "theme-light");
        assert_eq!(Theme::Dark.as_class(), "theme-dark");
        assert_eq!(Theme::Auto.as_class(), "theme-auto");
    }
}
