/// Elevation shadow scale.
///
/// Shadows communicate visual hierarchy and z-depth. Use the named scale —
/// it maps to appropriate platform-native elevation on each backend.

/// Named shadow/elevation tokens.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Shadow {
    /// No shadow — flat, on-surface elements.
    None,
    /// Subtle drop shadow — slightly elevated cards.
    Sm,
    /// Standard card shadow — interactive elements.
    Md,
    /// Prominent shadow — dialogs, dropdowns, popovers.
    Lg,
    /// Maximum elevation — toasts, context menus, tooltips.
    Xl,
}

/// A fully-resolved shadow specification.
#[derive(Debug, Clone)]
pub struct ShadowSpec {
    /// X offset in dp.
    pub offset_x: f32,
    /// Y offset in dp (positive = down).
    pub offset_y: f32,
    /// Blur radius in dp.
    pub blur: f32,
    /// Spread radius in dp.
    pub spread: f32,
    /// Shadow color (RGBA).
    pub color: (u8, u8, u8, f32),
}

impl ShadowSpec {
    /// CSS box-shadow string for this spec.
    pub fn to_css(&self) -> String {
        format!(
            "{}px {}px {}px {}px rgba({},{},{},{})",
            self.offset_x,
            self.offset_y,
            self.blur,
            self.spread,
            self.color.0,
            self.color.1,
            self.color.2,
            self.color.3,
        )
    }
}

/// Maps Shadow tokens to ShadowSpecs.
#[derive(Debug, Clone)]
pub struct ShadowScale {
    pub sm: ShadowSpec,
    pub md: ShadowSpec,
    pub lg: ShadowSpec,
    pub xl: ShadowSpec,
}

impl ShadowScale {
    /// Default light-mode shadow scale.
    pub fn light() -> Self {
        Self {
            sm: ShadowSpec {
                offset_x: 0.0,
                offset_y: 1.0,
                blur: 3.0,
                spread: 0.0,
                color: (0, 0, 0, 0.12),
            },
            md: ShadowSpec {
                offset_x: 0.0,
                offset_y: 4.0,
                blur: 12.0,
                spread: -2.0,
                color: (0, 0, 0, 0.15),
            },
            lg: ShadowSpec {
                offset_x: 0.0,
                offset_y: 8.0,
                blur: 24.0,
                spread: -4.0,
                color: (0, 0, 0, 0.18),
            },
            xl: ShadowSpec {
                offset_x: 0.0,
                offset_y: 16.0,
                blur: 48.0,
                spread: -8.0,
                color: (0, 0, 0, 0.22),
            },
        }
    }

    /// Default dark-mode shadow scale (more subtle, less visible on dark bg).
    pub fn dark() -> Self {
        Self {
            sm: ShadowSpec {
                offset_x: 0.0,
                offset_y: 1.0,
                blur: 3.0,
                spread: 0.0,
                color: (0, 0, 0, 0.3),
            },
            md: ShadowSpec {
                offset_x: 0.0,
                offset_y: 4.0,
                blur: 12.0,
                spread: -2.0,
                color: (0, 0, 0, 0.4),
            },
            lg: ShadowSpec {
                offset_x: 0.0,
                offset_y: 8.0,
                blur: 24.0,
                spread: -4.0,
                color: (0, 0, 0, 0.5),
            },
            xl: ShadowSpec {
                offset_x: 0.0,
                offset_y: 16.0,
                blur: 48.0,
                spread: -8.0,
                color: (0, 0, 0, 0.6),
            },
        }
    }

    /// Resolve a Shadow token to a ShadowSpec.
    /// Returns None for Shadow::None (no spec needed).
    pub fn resolve(&self, shadow: &Shadow) -> Option<&ShadowSpec> {
        match shadow {
            Shadow::None => None,
            Shadow::Sm => Some(&self.sm),
            Shadow::Md => Some(&self.md),
            Shadow::Lg => Some(&self.lg),
            Shadow::Xl => Some(&self.xl),
        }
    }
}

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

    #[test]
    fn shadow_none_resolves_to_none() {
        let scale = ShadowScale::light();
        assert!(scale.resolve(&Shadow::None).is_none());
    }

    #[test]
    fn shadow_sm_resolves() {
        let scale = ShadowScale::light();
        let spec = scale.resolve(&Shadow::Sm).unwrap();
        assert!(spec.blur > 0.0);
    }

    #[test]
    fn shadow_xl_has_larger_blur_than_sm() {
        let scale = ShadowScale::light();
        let sm = scale.resolve(&Shadow::Sm).unwrap();
        let xl = scale.resolve(&Shadow::Xl).unwrap();
        assert!(xl.blur > sm.blur);
    }

    #[test]
    fn shadow_spec_to_css() {
        let spec = ShadowSpec {
            offset_x: 0.0,
            offset_y: 4.0,
            blur: 12.0,
            spread: -2.0,
            color: (0, 0, 0, 0.15),
        };
        let css = spec.to_css();
        assert!(css.contains("rgba(0,0,0,0.15)"));
    }

    #[test]
    fn dark_shadows_are_more_opaque() {
        let light = ShadowScale::light();
        let dark = ShadowScale::dark();
        let l_alpha = light.resolve(&Shadow::Md).unwrap().color.3;
        let d_alpha = dark.resolve(&Shadow::Md).unwrap().color.3;
        assert!(d_alpha > l_alpha);
    }
}
