/// StyleSheet — named style rules for components.
///
/// Instead of applying modifiers inline everywhere, you can define named
/// style rules in a StyleSheet and apply them with `.apply("card")`.
/// This is the el-ui equivalent of CSS class names, but type-safe and
/// resolved at compile time.

use std::collections::HashMap;
use crate::modifier::StyleSet;

/// A stylesheet: a named collection of StyleSet rules.
#[derive(Debug, Clone, Default)]
pub struct StyleSheet {
    rules: HashMap<String, StyleSet>,
}

impl StyleSheet {
    pub fn new() -> Self {
        Self::default()
    }

    /// Define a named style rule.
    pub fn define(&mut self, name: impl Into<String>, style: StyleSet) -> &mut Self {
        self.rules.insert(name.into(), style);
        self
    }

    /// Look up a named style rule.
    /// Returns None if the rule doesn't exist.
    pub fn get(&self, name: &str) -> Option<&StyleSet> {
        self.rules.get(name)
    }

    /// Apply a named style to a StyleSet by merging — caller's explicit
    /// values win, the stylesheet fills in the rest.
    ///
    /// This is an additive operation: fields that are Some in the named
    /// rule overwrite fields that are None in the target.
    pub fn apply(&self, name: &str, target: &mut StyleSet) {
        if let Some(rule) = self.rules.get(name) {
            if target.padding.is_none() {
                target.padding = rule.padding;
            }
            if target.margin.is_none() {
                target.margin = rule.margin;
            }
            if target.background.is_none() {
                target.background = rule.background.clone();
            }
            if target.foreground.is_none() {
                target.foreground = rule.foreground.clone();
            }
            if target.font.is_none() {
                target.font = rule.font.clone();
            }
            if target.radius.is_none() {
                target.radius = rule.radius;
            }
            if target.shadow.is_none() {
                target.shadow = rule.shadow;
            }
            if target.opacity.is_none() {
                target.opacity = rule.opacity;
            }
            if target.border_width.is_none() {
                target.border_width = rule.border_width;
            }
            if target.border_color.is_none() {
                target.border_color = rule.border_color.clone();
            }
            if target.width.is_none() {
                target.width = rule.width.clone();
            }
            if target.height.is_none() {
                target.height = rule.height.clone();
            }
            if target.max_width.is_none() {
                target.max_width = rule.max_width.clone();
            }
        }
    }

    /// The number of rules defined.
    pub fn len(&self) -> usize {
        self.rules.len()
    }

    pub fn is_empty(&self) -> bool {
        self.rules.is_empty()
    }
}

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

    fn card_style() -> StyleSet {
        let mut s = StyleSet::default();
        s.padding = Some([16; 4]);
        s.radius = Some([8; 4]);
        s.background = Some(Color::Surface);
        s
    }

    #[test]
    fn stylesheet_define_and_get() {
        let mut ss = StyleSheet::new();
        ss.define("card", card_style());
        assert!(ss.get("card").is_some());
    }

    #[test]
    fn stylesheet_missing_rule_returns_none() {
        let ss = StyleSheet::new();
        assert!(ss.get("nonexistent").is_none());
    }

    #[test]
    fn stylesheet_apply_fills_missing_values() {
        let mut ss = StyleSheet::new();
        ss.define("card", card_style());

        let mut target = StyleSet::default();
        ss.apply("card", &mut target);

        assert_eq!(target.padding, Some([16; 4]));
        assert_eq!(target.background, Some(Color::Surface));
    }

    #[test]
    fn stylesheet_apply_does_not_overwrite_existing() {
        let mut ss = StyleSheet::new();
        ss.define("card", card_style());

        let mut target = StyleSet::default();
        target.background = Some(Color::Primary); // already set
        ss.apply("card", &mut target);

        // Should NOT be overwritten by the stylesheet's Surface
        assert_eq!(target.background, Some(Color::Primary));
    }

    #[test]
    fn stylesheet_len() {
        let mut ss = StyleSheet::new();
        assert_eq!(ss.len(), 0);
        ss.define("card", card_style());
        assert_eq!(ss.len(), 1);
    }

    #[test]
    fn stylesheet_apply_nonexistent_is_noop() {
        let ss = StyleSheet::new();
        let mut target = StyleSet::default();
        ss.apply("does-not-exist", &mut target); // should not panic
        assert!(target.padding.is_none());
    }
}
