/// Theme — the single source of truth for all visual values.
///
/// Components never hardcode colors, sizes, or shadows. They reference
/// semantic tokens; the Theme resolves them. Swap the theme node at the
/// root of the experience and every component updates automatically.

use crate::color::ColorScheme;
use crate::radius::RadiusScale;
use crate::shadow::ShadowScale;
use crate::spacing::SpacingScale;
use crate::typography::TypographyScheme;

/// Whether the theme follows the OS preference or is explicitly light/dark.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ThemeMode {
    /// Explicit light mode.
    Light,
    /// Explicit dark mode.
    Dark,
    /// Follow the OS preference (default).
    System,
}

/// The complete design system for an experience.
///
/// One Theme per experience, flowing down through the component tree.
/// Components read from it; they do not hold their own style values.
#[derive(Debug, Clone)]
pub struct Theme {
    pub mode: ThemeMode,
    pub colors: ColorScheme,
    pub typography: TypographyScheme,
    pub spacing: SpacingScale,
    pub radius: RadiusScale,
    pub shadows: ShadowScale,
}

impl Theme {
    /// The default light theme.
    pub fn default_light() -> Self {
        Self {
            mode: ThemeMode::Light,
            colors: ColorScheme::light(),
            typography: TypographyScheme::default(),
            spacing: SpacingScale::default(),
            radius: RadiusScale::default(),
            shadows: ShadowScale::light(),
        }
    }

    /// The default dark theme.
    pub fn default_dark() -> Self {
        Self {
            mode: ThemeMode::Dark,
            colors: ColorScheme::dark(),
            typography: TypographyScheme::default(),
            spacing: SpacingScale::default(),
            radius: RadiusScale::default(),
            shadows: ShadowScale::dark(),
        }
    }

    /// A system-following theme (uses light values as the base;
    /// the runtime swaps to dark when the OS signals dark mode).
    pub fn system() -> Self {
        Self {
            mode: ThemeMode::System,
            colors: ColorScheme::light(),
            typography: TypographyScheme::default(),
            spacing: SpacingScale::default(),
            radius: RadiusScale::default(),
            shadows: ShadowScale::light(),
        }
    }

    /// Return a copy of this theme in the specified mode,
    /// updating colors and shadows to match.
    pub fn with_mode(&self, mode: ThemeMode) -> Self {
        let (colors, shadows) = match &mode {
            ThemeMode::Dark => (ColorScheme::dark(), ShadowScale::dark()),
            ThemeMode::Light | ThemeMode::System => {
                (ColorScheme::light(), ShadowScale::light())
            }
        };
        Self {
            mode,
            colors,
            shadows,
            typography: self.typography.clone(),
            spacing: self.spacing.clone(),
            radius: self.radius.clone(),
        }
    }

    /// Whether dark mode is currently active.
    pub fn is_dark(&self) -> bool {
        matches!(self.mode, ThemeMode::Dark)
    }
}

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

    #[test]
    fn light_theme_not_dark() {
        assert!(!Theme::default_light().is_dark());
    }

    #[test]
    fn dark_theme_is_dark() {
        assert!(Theme::default_dark().is_dark());
    }

    #[test]
    fn system_theme_mode() {
        assert_eq!(Theme::system().mode, ThemeMode::System);
    }

    #[test]
    fn with_mode_switches_colors() {
        let light = Theme::default_light();
        let dark = light.with_mode(ThemeMode::Dark);
        let (_, _, _, _la) = light.colors.resolve(&Color::Background);
        let (r, g, b, _) = dark.colors.resolve(&Color::Background);
        // Dark background should be dark (low values)
        assert!(r < 50 && g < 50 && b < 50);
    }

    #[test]
    fn theme_spacing_resolves() {
        use crate::spacing::Spacing;
        let theme = Theme::default_light();
        assert_eq!(theme.spacing.resolve(&Spacing::Lg), 16);
    }

    #[test]
    fn theme_radius_resolves() {
        use crate::radius::Radius;
        let theme = Theme::default_light();
        assert_eq!(theme.radius.resolve(&Radius::Md), 8);
    }
}
