//! CSS-inspired Specificity calculation for Wasot selectors
//!
//! This module implements specificity calculation inspired by CSS rules.
//! Specificity is represented as a tuple (a, b, c) where:
//! - a: number of ID selectors
//! - b: number of class selectors, attributes, and pseudo-classes
//! - c: number of type selectors and pseudo-elements

use super::ast::{
    CompoundSelector, PseudoClassSelector, SelectorGroup, SelectorPart, SimpleSelector,
    SimpleSelectorGroup, TagSelector,
};

/// Represents the specificity of a Wasot selector as a 3-tuple
/// (ids, classes_attributes_pseudos, types)
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Specificity(pub u32, pub u32, pub u32);

impl Specificity {
    /// Creates a new Specificity with the given values
    pub fn new(ids: u32, classes: u32, types: u32) -> Self {
        Specificity(ids, classes, types)
    }

    /// Returns zero specificity
    pub fn zero() -> Self {
        Specificity(0, 0, 0)
    }

    /// Adds two specificities together
    pub fn add(&self, other: &Specificity) -> Specificity {
        Specificity(
            self.0.saturating_add(other.0),
            self.1.saturating_add(other.1),
            self.2.saturating_add(other.2),
        )
    }

    /// Formats the specificity as a string for debugging
    /// Format the specificity as a string in the form "(a,b,c)"
    pub fn as_string(&self) -> String {
        format!("({},{},{})", self.0, self.1, self.2)
    }
}

/// Calculate the specificity of a SelectorPart
pub fn calculate_selector_part(part: &SelectorPart) -> Specificity {
    match part {
        SelectorPart::Simple(selector) => calculate_simple_selector(selector),
        SelectorPart::Combinator(_) => Specificity::zero(), // Combinators don't affect specificity
    }
}

/// Calculate the specificity of a SimpleSelector
pub fn calculate_simple_selector(selector: &SimpleSelector) -> Specificity {
    let mut specificity = Specificity::zero();

    // Count ID selector
    if selector.id.is_some() {
        specificity.0 += 1;
    }

    // Count type selector
    if let Some(tag) = &selector.tag {
        match tag {
            TagSelector::Universal => {
                // Universal selector (*) has zero specificity
            }
            TagSelector::TypeName(_) => {
                specificity.2 += 1;
            }
        }
    }

    // Count class selectors
    specificity.1 += selector.classes.len() as u32;

    // Count attribute selectors (all variants count as 1)
    specificity.1 += selector.attributes.len() as u32;

    // Count attribute presence selectors (simplified version)
    specificity.1 += selector.attribute_presence.len() as u32;

    // Count attribute value selectors (simplified version)
    specificity.1 += selector.attribute_values.len() as u32;

    // Count pseudo-class selectors
    for pseudo in &selector.pseudo_classes {
        match pseudo {
            // :not() doesn't add to specificity itself, but its contents do
            PseudoClassSelector::Not(compound) => {
                specificity = specificity.add(&calculate_compound_selector(compound));
            }
            // All other pseudo-classes add to the class count
            PseudoClassSelector::FirstChild
            | PseudoClassSelector::LastChild
            | PseudoClassSelector::NthChild(_)
            | PseudoClassSelector::Simple(_)
            | PseudoClassSelector::Nth(_, _) => {
                specificity.1 += 1;
            }
        }
    }

    // Handle nth-child from simplified version
    if selector.nth_child.is_some() {
        specificity.1 += 1;
    }

    // Handle :not() selectors from simplified version
    for not_selector in &selector.not_selectors {
        // :not() doesn't add to specificity, but its contents do
        specificity = specificity.add(&calculate_simple_selector(not_selector));
    }

    specificity
}

/// Calculate the specificity of a CompoundSelector
pub fn calculate_compound_selector(selector: &CompoundSelector) -> Specificity {
    let mut specificity = calculate_simple_selector(&selector.base);

    // Add specificity from all parts in the chain
    for (_, simple_selector) in &selector.parts {
        specificity = specificity.add(&calculate_simple_selector(simple_selector));
    }

    specificity
}

/// Calculate the specificity of a SimpleSelectorGroup (used in demo/simplified version)
///
/// For a single selector chain (e.g., "div > p.intro"), this adds up all the parts.
/// For multiple selector chains separated by commas (OR selectors), this would need
/// to be handled differently - typically by calculating each chain separately and
/// taking the maximum.
pub fn calculate_simple_selector_group(group: &SimpleSelectorGroup) -> Specificity {
    // SimpleSelectorGroup in the current implementation represents a single selector chain
    // so we add all parts together
    let mut specificity = Specificity::zero();

    for part in &group.selectors {
        specificity = specificity.add(&calculate_selector_part(part));
    }

    specificity
}

/// Calculate the specificity of a SelectorGroup (full version)
/// Returns the maximum specificity among all selectors in the group
pub fn calculate_selector_group(group: &SelectorGroup) -> Specificity {
    group
        .selectors
        .iter()
        .map(calculate_compound_selector)
        .max()
        .unwrap_or_else(Specificity::zero)
}

#[cfg(test)]
mod tests {
    use super::super::ast::{AttributeSelector, Combinator};
    use super::*;

    #[test]
    fn test_specificity_comparison() {
        // Test that Specificity comparison works correctly
        let s1 = Specificity(1, 0, 0); // ID selector
        let s2 = Specificity(0, 10, 10); // Many classes and types
        assert!(s1 > s2, "ID should beat any number of classes/types");

        let s3 = Specificity(0, 2, 1);
        let s4 = Specificity(0, 1, 5);
        assert!(s3 > s4, "More classes should beat more types");

        let s5 = Specificity(0, 1, 1);
        let s6 = Specificity(0, 1, 1);
        assert_eq!(s5, s6, "Same specificity should be equal");
    }

    #[test]
    fn test_universal_selector() {
        let selector = SimpleSelector {
            tag: Some(TagSelector::Universal),
            ..Default::default()
        };
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 0, 0),
            "* should have zero specificity"
        );
    }

    #[test]
    fn test_type_selector() {
        let selector = SimpleSelector::builder().tag("div").build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 0, 1),
            "div should have (0,0,0,1)"
        );
    }

    #[test]
    fn test_class_selector() {
        let selector = SimpleSelector::builder().class("warning").build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 1, 0),
            ".warning should have (0,0,1,0)"
        );
    }

    #[test]
    fn test_id_selector() {
        let selector = SimpleSelector::builder().id("main").build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(1, 0, 0),
            "#main should have (0,1,0,0)"
        );
    }

    #[test]
    fn test_compound_selector() {
        let selector = SimpleSelector::builder()
            .tag("div")
            .id("main")
            .class("active")
            .build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(1, 1, 1),
            "div#main.active should have (0,1,1,1)"
        );
    }

    #[test]
    fn test_multiple_classes() {
        let selector = SimpleSelector::builder()
            .class("nav")
            .class("active")
            .class("primary")
            .build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 3, 0),
            "Three classes should have (0,0,3,0)"
        );
    }

    #[test]
    fn test_attribute_selectors() {
        let selector = SimpleSelector::builder()
            .attribute("disabled")
            .attribute_value("role", "navigation")
            .build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 2, 0),
            "Two attributes should have (0,0,2,0)"
        );
    }

    #[test]
    fn test_pseudo_class() {
        let selector = SimpleSelector::builder().nth_child(3).build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 1, 0),
            ":nth-child(3) should have (0,0,1,0)"
        );
    }

    #[test]
    fn test_not_pseudo_class() {
        // :not(.disabled)
        let selector = SimpleSelector::builder()
            .not(SimpleSelector::builder().class("disabled").build())
            .build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 1, 0),
            ":not(.disabled) should have (0,0,1,0) from the class inside"
        );

        // :not(#main)
        let selector = SimpleSelector::builder()
            .not(SimpleSelector::builder().id("main").build())
            .build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(1, 0, 0),
            ":not(#main) should have (0,1,0,0) from the ID inside"
        );

        // :not(div)
        let selector = SimpleSelector::builder()
            .not(SimpleSelector::builder().tag("div").build())
            .build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 0, 1),
            ":not(div) should have (0,0,0,1) from the type inside"
        );
    }

    #[test]
    fn test_complex_selector() {
        // div#main.active[role="navigation"]:hover:not(.disabled)
        let selector = SimpleSelector::builder()
            .tag("div")
            .id("main")
            .class("active")
            .attribute_value("role", "navigation")
            .not(SimpleSelector::builder().class("disabled").build())
            .build();

        // Add a pseudo-class manually for this test
        let mut selector = selector;
        selector
            .pseudo_classes
            .push(PseudoClassSelector::Simple("hover".to_string()));

        let specificity = calculate_simple_selector(&selector);
        // div(1) + #main(100) + .active(10) + [role](10) + :hover(10) + :not(.disabled)(10)
        // = (0,1,4,1)
        assert_eq!(
            specificity,
            Specificity(1, 4, 1),
            "Complex selector should have correct specificity"
        );
    }

    #[test]
    fn test_selector_chain_with_combinators() {
        // section > p.intro
        let group = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("section").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("p").class("intro").part(),
            ],
        };
        let specificity = calculate_simple_selector_group(&group);
        assert_eq!(
            specificity,
            Specificity(0, 1, 2),
            "section > p.intro should have (0,0,1,2)"
        );
    }

    #[test]
    fn test_descendant_selector() {
        // #header nav ul li
        let group = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().id("header").part(),
                SelectorPart::Combinator(Combinator::Descendant),
                SimpleSelector::builder().tag("nav").part(),
                SelectorPart::Combinator(Combinator::Descendant),
                SimpleSelector::builder().tag("ul").part(),
                SelectorPart::Combinator(Combinator::Descendant),
                SimpleSelector::builder().tag("li").part(),
            ],
        };
        let specificity = calculate_simple_selector_group(&group);
        assert_eq!(
            specificity,
            Specificity(1, 0, 3),
            "#header nav ul li should have (0,1,0,3)"
        );
    }

    #[test]
    fn test_compound_selector_full() {
        // Test CompoundSelector structure
        let compound = CompoundSelector {
            base: SimpleSelector::builder().tag("div").id("main").build(),
            parts: vec![
                (
                    Combinator::Child,
                    SimpleSelector::builder().tag("p").class("intro").build(),
                ),
                (
                    Combinator::Descendant,
                    SimpleSelector::builder().tag("span").build(),
                ),
            ],
        };
        let specificity = calculate_compound_selector(&compound);
        // div#main(0,1,0,1) + p.intro(0,0,1,1) + span(0,0,0,1) = (0,1,1,3)
        assert_eq!(
            specificity,
            Specificity(1, 1, 3),
            "Compound selector should sum all parts"
        );
    }

    #[test]
    fn test_selector_group() {
        // h1, h2, h3 - should return the maximum specificity
        let group = SelectorGroup {
            selectors: vec![
                CompoundSelector {
                    base: SimpleSelector::builder().tag("h1").build(),
                    parts: vec![],
                },
                CompoundSelector {
                    base: SimpleSelector::builder().tag("h2").id("title").build(),
                    parts: vec![],
                },
                CompoundSelector {
                    base: SimpleSelector::builder().tag("h3").build(),
                    parts: vec![],
                },
            ],
        };
        let specificity = calculate_selector_group(&group);
        // Should return h2#title specificity (0,1,0,1)
        assert_eq!(
            specificity,
            Specificity(1, 0, 1),
            "Selector group should return max specificity"
        );
    }

    #[test]
    fn test_empty_selector() {
        let selector = SimpleSelector::default();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 0, 0),
            "Empty selector should have zero specificity"
        );
    }

    #[test]
    fn test_pseudo_class_variants() {
        // Test FirstChild
        let selector = SimpleSelector {
            pseudo_classes: vec![PseudoClassSelector::FirstChild],
            ..Default::default()
        };
        assert_eq!(
            calculate_simple_selector(&selector),
            Specificity(0, 1, 0),
            ":first-child should have (0,0,1,0)"
        );

        // Test LastChild
        let selector = SimpleSelector {
            pseudo_classes: vec![PseudoClassSelector::LastChild],
            ..Default::default()
        };
        assert_eq!(
            calculate_simple_selector(&selector),
            Specificity(0, 1, 0),
            ":last-child should have (0,0,1,0)"
        );

        // Test NthChild
        let selector = SimpleSelector {
            pseudo_classes: vec![PseudoClassSelector::NthChild("2n+1".to_string())],
            ..Default::default()
        };
        assert_eq!(
            calculate_simple_selector(&selector),
            Specificity(0, 1, 0),
            ":nth-child(2n+1) should have (0,0,1,0)"
        );

        // Test multiple pseudo-classes
        let selector = SimpleSelector {
            pseudo_classes: vec![
                PseudoClassSelector::Simple("hover".to_string()),
                PseudoClassSelector::Simple("focus".to_string()),
                PseudoClassSelector::FirstChild,
            ],
            ..Default::default()
        };
        assert_eq!(
            calculate_simple_selector(&selector),
            Specificity(0, 3, 0),
            "Multiple pseudo-classes should sum"
        );
    }

    #[test]
    fn test_attribute_selector_variants() {
        // Test different attribute selector types (all count as 1)
        let selector = SimpleSelector {
            attributes: vec![
                AttributeSelector::Presence("disabled".to_string()),
                AttributeSelector::ExactMatch {
                    key: "role".to_string(),
                    value: "navigation".to_string(),
                },
                AttributeSelector::PrefixMatch {
                    key: "lang".to_string(),
                    value: "en".to_string(),
                },
            ],
            ..Default::default()
        };

        assert_eq!(
            calculate_simple_selector(&selector),
            Specificity(0, 3, 0),
            "Three attribute selectors should have (0,0,3,0)"
        );
    }

    #[test]
    fn test_specificity_add() {
        let s1 = Specificity(1, 2, 3);
        let s2 = Specificity(2, 3, 4);
        let result = s1.add(&s2);
        assert_eq!(result, Specificity(3, 5, 7), "Add should sum components");

        // Test saturation
        let max = Specificity(u32::MAX, u32::MAX, u32::MAX);
        let one = Specificity(1, 1, 1);
        let saturated = max.add(&one);
        assert_eq!(saturated, max, "Should saturate at u32::MAX");
    }

    #[test]
    fn test_specificity_to_string() {
        let s = Specificity(1, 2, 3);
        assert_eq!(s.as_string(), "(1,2,3)");
    }

    #[test]
    fn test_nested_not_selector() {
        // :not(div#main.active)
        let selector = SimpleSelector::builder()
            .not(
                SimpleSelector::builder()
                    .tag("div")
                    .id("main")
                    .class("active")
                    .build(),
            )
            .build();
        let specificity = calculate_simple_selector(&selector);
        // Should count the contents of :not()
        assert_eq!(
            specificity,
            Specificity(1, 1, 1),
            ":not(div#main.active) should have (0,1,1,1)"
        );
    }

    #[test]
    fn test_multiple_not_selectors() {
        // :not(.disabled):not(.hidden)
        let selector = SimpleSelector::builder()
            .not(SimpleSelector::builder().class("disabled").build())
            .not(SimpleSelector::builder().class("hidden").build())
            .build();
        let specificity = calculate_simple_selector(&selector);
        assert_eq!(
            specificity,
            Specificity(0, 2, 0),
            "Multiple :not() should sum their contents"
        );
    }

    #[test]
    fn test_real_world_selectors() {
        // nav.primary > ul > li.active > a[href^="https"]:hover
        let group = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("nav").class("primary").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("ul").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("li").class("active").part(),
                SelectorPart::Combinator(Combinator::Child),
                {
                    let mut s = SimpleSelector::builder().tag("a").build();
                    s.attributes.push(AttributeSelector::StartsWith {
                        key: "href".to_string(),
                        value: "https".to_string(),
                    });
                    s.pseudo_classes
                        .push(PseudoClassSelector::Simple("hover".to_string()));
                    SelectorPart::Simple(Box::new(s))
                },
            ],
        };
        let specificity = calculate_simple_selector_group(&group);
        // nav(1) + .primary(10) + ul(1) + li(1) + .active(10) + a(1) + [href](10) + :hover(10)
        // = (0,0,4,4)
        assert_eq!(
            specificity,
            Specificity(0, 4, 4),
            "Complex real-world selector"
        );
    }

    // ==============================================================================
    // Additional Tests for Specific Coverage Lines
    // ==============================================================================

    #[test]
    fn test_specificity_new() {
        // Test lines 21-22 - Specificity::new function
        let spec = Specificity::new(5, 10, 15);
        assert_eq!(spec.0, 5);
        assert_eq!(spec.1, 10);
        assert_eq!(spec.2, 15);
        assert_eq!(spec, Specificity(5, 10, 15));
    }

    #[test]
    fn test_specificity_zero() {
        // Test lines 26-27 - Specificity::zero function
        let spec = Specificity::zero();
        assert_eq!(spec, Specificity(0, 0, 0));
    }

    #[test]
    fn test_specificity_add_detailed() {
        // Test line 31, lines 33-35 - add function with saturating_add
        let s1 = Specificity(1, 2, 3);
        let s2 = Specificity(4, 5, 6);
        let result = s1.add(&s2);

        // This hits lines 33-35 specifically
        assert_eq!(result, Specificity(5, 7, 9));

        // Test edge case with large numbers to ensure saturating_add
        let large1 = Specificity(u32::MAX - 1, u32::MAX - 1, u32::MAX - 1);
        let large2 = Specificity(5, 5, 5);
        let saturated = large1.add(&large2);
        assert_eq!(saturated, Specificity(u32::MAX, u32::MAX, u32::MAX));
    }

    #[test]
    fn test_specificity_as_string() {
        // Test lines 41-42 - as_string function
        let spec = Specificity(100, 25, 7);
        let formatted = spec.as_string();
        assert_eq!(formatted, "(100,25,7)");

        // Test zero case
        let zero_spec = Specificity::zero();
        assert_eq!(zero_spec.as_string(), "(0,0,0)");

        // Test single digit case
        let single = Specificity(1, 2, 3);
        assert_eq!(single.as_string(), "(1,2,3)");
    }

    #[test]
    fn test_calculate_selector_part_detailed() {
        // Test lines 47-50 - calculate_selector_part function

        // Test Simple selector case (line 49)
        let simple_selector = SimpleSelector::builder().tag("div").build();
        let simple_part = SelectorPart::Simple(Box::new(simple_selector));
        let spec = calculate_selector_part(&simple_part);
        assert_eq!(spec, Specificity(0, 0, 1));

        // Test Combinator case (line 50)
        let combinator_part = SelectorPart::Combinator(Combinator::Child);
        let spec = calculate_selector_part(&combinator_part);
        assert_eq!(spec, Specificity::zero());

        // Test with different combinators
        let descendant_part = SelectorPart::Combinator(Combinator::Descendant);
        assert_eq!(
            calculate_selector_part(&descendant_part),
            Specificity::zero()
        );

        let sibling_part = SelectorPart::Combinator(Combinator::AdjacentSibling);
        assert_eq!(calculate_selector_part(&sibling_part), Specificity::zero());
    }

    #[test]
    fn test_calculate_simple_selector_detailed() {
        // Test lines 55-56 - function start and zero initialization
        let empty_selector = SimpleSelector::default();
        let spec = calculate_simple_selector(&empty_selector);
        assert_eq!(spec, Specificity::zero());

        // Test lines 59-60 - ID selector check
        let id_selector = SimpleSelector::builder().id("test").build();
        let spec = calculate_simple_selector(&id_selector);
        assert_eq!(spec.0, 1); // ID count should be 1

        // Test lines 64-65 - tag selector check with None case
        let no_tag_selector = SimpleSelector::default();
        let spec = calculate_simple_selector(&no_tag_selector);
        assert_eq!(spec.2, 0); // Type count should be 0

        // Test lines 69-70 - TypeName case
        let type_selector = SimpleSelector::builder().tag("span").build();
        let spec = calculate_simple_selector(&type_selector);
        assert_eq!(spec.2, 1); // Type count should be 1
    }

    #[test]
    fn test_universal_selector_specificity() {
        // Test lines 66-68 - Universal selector case (no increment)
        let universal_selector = SimpleSelector {
            tag: Some(TagSelector::Universal),
            ..Default::default()
        };
        let spec = calculate_simple_selector(&universal_selector);
        assert_eq!(spec, Specificity(0, 0, 0)); // Universal should not increment type count
    }

    #[test]
    fn test_classes_count() {
        // Test line 76 - classes count
        let multi_class = SimpleSelector::builder()
            .class("one")
            .class("two")
            .class("three")
            .build();
        let spec = calculate_simple_selector(&multi_class);
        assert_eq!(spec.1, 3); // Should count 3 classes

        // Test empty classes
        let no_classes = SimpleSelector::default();
        let spec = calculate_simple_selector(&no_classes);
        assert_eq!(spec.1, 0); // Should be 0 for no classes
    }

    #[test]
    fn test_attribute_counts() {
        // Test line 79 - attributes.len()
        let selector = SimpleSelector {
            attributes: vec![
                AttributeSelector::Presence("attr1".to_string()),
                AttributeSelector::ExactMatch {
                    key: "attr2".to_string(),
                    value: "value".to_string(),
                },
            ],
            ..Default::default()
        };
        let spec = calculate_simple_selector(&selector);
        assert!(spec.1 >= 2); // Should count attributes

        // Test line 82 - attribute_presence.len()
        let presence_selector = SimpleSelector::builder()
            .attribute("disabled")
            .attribute("checked")
            .build();
        let spec = calculate_simple_selector(&presence_selector);
        assert!(spec.1 >= 2); // Should count presence attributes

        // Test line 85 - attribute_values.len()
        let value_selector = SimpleSelector::builder()
            .attribute_value("role", "button")
            .attribute_value("type", "submit")
            .build();
        let spec = calculate_simple_selector(&value_selector);
        assert!(spec.1 >= 2); // Should count value attributes
    }

    #[test]
    fn test_pseudo_class_specificity_detailed() {
        // Test lines 88-103 - pseudo-class handling

        // Test line 91-92 - Not pseudo-class with compound selector
        let compound = CompoundSelector {
            base: SimpleSelector::builder().tag("div").class("test").build(),
            parts: vec![],
        };
        let not_selector = SimpleSelector {
            pseudo_classes: vec![PseudoClassSelector::Not(Box::new(compound))],
            ..Default::default()
        };
        let spec = calculate_simple_selector(&not_selector);
        // Should include specificity from the compound selector inside :not()
        assert!(spec.1 >= 1); // At least the class from inside :not()
        assert!(spec.2 >= 1); // At least the tag from inside :not()

        // Test lines 95-100 - other pseudo-class types
        let multi_pseudo = SimpleSelector {
            pseudo_classes: vec![
                PseudoClassSelector::FirstChild,
                PseudoClassSelector::LastChild,
                PseudoClassSelector::NthChild("2n+1".to_string()),
                PseudoClassSelector::Simple("hover".to_string()),
                PseudoClassSelector::Nth("child".to_string(), "3".to_string()),
            ],
            ..Default::default()
        };
        let spec = calculate_simple_selector(&multi_pseudo);
        assert_eq!(spec.1, 5); // Should count all 5 pseudo-classes
    }

    #[test]
    fn test_nth_child_simplified() {
        // Test lines 106-107 - nth_child from simplified version
        let nth_selector = SimpleSelector::builder().nth_child(2).build();
        let spec = calculate_simple_selector(&nth_selector);
        assert!(spec.1 >= 1); // Should increment class count for nth-child
    }

    #[test]
    fn test_not_selectors_simplified() {
        // Test lines 111-113 - not_selectors from simplified version
        let not_class = SimpleSelector::builder().class("hidden").build();
        let not_id = SimpleSelector::builder().id("modal").build();

        let selector_with_nots = SimpleSelector {
            not_selectors: vec![not_class, not_id],
            ..Default::default()
        };
        let spec = calculate_simple_selector(&selector_with_nots);
        // Should include specificity from both not selectors
        assert_eq!(spec.0, 1); // ID from :not(#modal)
        assert_eq!(spec.1, 1); // Class from :not(.hidden)
    }

    #[test]
    fn test_calculate_compound_selector_detailed() {
        // Test lines 120-128 - compound selector calculation

        // Test line 121 - base selector calculation
        let compound = CompoundSelector {
            base: SimpleSelector::builder().tag("div").id("main").build(),
            parts: vec![],
        };
        let spec = calculate_compound_selector(&compound);
        assert_eq!(spec, Specificity(1, 0, 1)); // ID + tag

        // Test lines 124-125 - parts iteration and addition
        let complex_compound = CompoundSelector {
            base: SimpleSelector::builder().tag("section").build(),
            parts: vec![
                (
                    Combinator::Child,
                    SimpleSelector::builder().tag("p").class("intro").build(),
                ),
                (
                    Combinator::Descendant,
                    SimpleSelector::builder().id("content").build(),
                ),
            ],
        };
        let spec = calculate_compound_selector(&complex_compound);
        // section(1) + p.intro(0,1,1) + #content(1,0,0) = (1,1,2)
        assert_eq!(spec, Specificity(1, 1, 2));
    }

    #[test]
    fn test_calculate_simple_selector_group_detailed() {
        // Test lines 137, 140, 142-143, 146 - simple selector group calculation

        // Test line 140 - zero initialization
        let empty_group = SimpleSelectorGroup { selectors: vec![] };
        let spec = calculate_simple_selector_group(&empty_group);
        assert_eq!(spec, Specificity::zero());

        // Test lines 142-143 - iteration and addition
        let group = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("div").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().id("main").class("active").part(),
            ],
        };
        let spec = calculate_simple_selector_group(&group);
        // div(0,0,1) + combinator(0,0,0) + #main.active(1,1,0) = (1,1,1)
        assert_eq!(spec, Specificity(1, 1, 1));
    }

    #[test]
    fn test_calculate_selector_group_detailed() {
        // Test lines 151-152 - selector group with max calculation

        // Test empty group
        let empty_group = SelectorGroup { selectors: vec![] };
        let spec = calculate_selector_group(&empty_group);
        assert_eq!(spec, Specificity::zero());

        // Test group with multiple selectors - should return max
        let group = SelectorGroup {
            selectors: vec![
                CompoundSelector {
                    base: SimpleSelector::builder().tag("p").build(), // (0,0,1)
                    parts: vec![],
                },
                CompoundSelector {
                    base: SimpleSelector::builder().id("main").build(), // (1,0,0)
                    parts: vec![],
                },
                CompoundSelector {
                    base: SimpleSelector::builder()
                        .class("active")
                        .class("primary")
                        .build(), // (0,2,0)
                    parts: vec![],
                },
            ],
        };
        let spec = calculate_selector_group(&group);
        // Should return the ID selector's specificity as it's highest
        assert_eq!(spec, Specificity(1, 0, 0));
    }
}
