//! Abstract Syntax Tree types for CSS-like selectors in Wasot
//!
//! This module contains the AST structures used to represent CSS-like selectors
//! for Wasot documents. These types support:
//! - Type selectors (e.g., `paragraph`, `div`)
//! - ID selectors (e.g., `#main`)
//! - Class selectors (e.g., `.highlight`)
//! - Attribute selectors (presence and value checks)
//! - Pseudo-classes (`:nth-child`, `:not()`)
//! - Combinators (descendant, child)

use std::collections::{HashMap, HashSet};

use crate::MetaLine;

// ==============================================================================
// Additional types for compatibility with selector_consumer and selector_emitter
// These are placeholder types until the full parser is implemented
// ==============================================================================

/// Tag selector type - either universal (*) or a specific type name
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TagSelector {
    Universal,
    TypeName(String),
}

/// Attribute selector variants
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttributeSelector {
    Presence(String),
    NegatedPresence(String),
    ExactMatch { key: String, value: String },
    NegatedMatch { key: String, value: String },
    PrefixMatch { key: String, value: String },
    SuffixMatch { key: String, value: String },
    SubstringMatch { key: String, value: String },
    TokenMatch { key: String, value: String },
    StartsWith { key: String, value: String },
    EndsWith { key: String, value: String },
    Contains { key: String, value: String },
}

/// Pseudo-class selector variants
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PseudoClassSelector {
    FirstChild,
    LastChild,
    NthChild(String),
    Not(Box<CompoundSelector>),
    Simple(String),
    Nth(String, String),
}

/// Compound selector - combines a base selector with additional parts
/// This is used by the full parser implementation
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompoundSelector {
    pub base: SimpleSelector,
    pub parts: Vec<(Combinator, SimpleSelector)>,
}

/// A group of selectors - in full CSS this would be comma-separated (OR selectors)
/// Example: `h1, h2, h3` or `section > p, article > div`
///
/// This version is compatible with both the simplified demo (using Vec<SelectorPart>)
/// and the full parser (using Vec<CompoundSelector>)
#[derive(Debug, PartialEq, Clone)]
pub struct SelectorGroup {
    /// For the full parser implementation this should contain CompoundSelectors
    /// For the demo, this can be created from Vec<SelectorPart> via conversion
    pub selectors: Vec<CompoundSelector>,
}

/// Simplified selector group for demo purposes
/// This is what the css_selector_demo uses
#[derive(Debug, PartialEq, Clone)]
pub struct SimpleSelectorGroup {
    pub selectors: Vec<SelectorPart>,
}

/// Represents either a simple selector or a combinator in the selector chain
/// This is a simplified representation - the full parser uses a more structured approach
/// with CompoundSelector containing a base and parts array.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SelectorPart {
    /// A simple selector that can match elements by type, ID, classes, attributes, etc.
    Simple(Box<SimpleSelector>),
    /// A combinator that defines the relationship between selectors
    Combinator(Combinator),
}

/// Combinator types for connecting selectors
/// Defines the relationship between elements in the document tree
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Combinator {
    /// Descendant combinator (whitespace) - matches any descendant
    /// Example: `section paragraph` matches any paragraph inside a section at any depth
    Descendant,
    /// Child combinator (>) - matches direct children only  
    /// Example: `section > paragraph` matches paragraphs that are immediate children of section
    Child,
    /// Adjacent sibling combinator (+) - matches immediately following sibling
    /// Example: `h1 + paragraph` matches the first paragraph after an h1
    AdjacentSibling,
    /// General sibling combinator (~) - matches any following sibling
    /// Example: `h1 ~ paragraph` matches any paragraph that follows an h1 at the same level
    GeneralSibling,
}

/// A simple selector that can contain type, ID, classes, attributes, and pseudo-classes
///
/// This structure supports both the simplified demo version and the full parser.
/// Some fields are used by the demo (attribute_presence, attribute_values, nth_child, not_selectors)
/// while others are used by the full parser implementation (tag as TagSelector, attributes, pseudo_classes).
///
/// Example: `div#main.highlight[role="navigation"]:first-child`
/// would match a div with id="main", class="highlight", role="navigation" that is the first child
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct SimpleSelector {
    /// ID selector - matches elements with specific ID (e.g., #main)
    pub id: Option<String>,
    /// Type/tag selector - for simplified version this is Option<String>,
    /// for full parser this would use TagSelector enum
    pub tag: Option<TagSelector>,
    /// Class selectors - matches elements with these classes (e.g., .highlight, .active)
    pub classes: HashSet<String>,
    /// Attribute selectors for full parser
    pub attributes: Vec<AttributeSelector>,
    /// Pseudo-class selectors for full parser
    pub pseudo_classes: Vec<PseudoClassSelector>,
    /// Attribute presence selectors - simplified version (e.g., [disabled])
    pub attribute_presence: Vec<String>,
    /// Attribute value selectors - simplified version (e.g., [lang="en"])
    pub attribute_values: HashMap<String, String>,
    /// Positional pseudo-class - simplified version (1-based, as in CSS)
    pub nth_child: Option<usize>,
    /// Negation pseudo-class - simplified version
    pub not_selectors: Vec<SimpleSelector>,
}

/// Represents an element in the document tree during matching
/// Tracks both the element's metadata and its position among siblings
#[derive(Debug, Clone, PartialEq)]
pub struct StackFrame {
    /// The MetaLine containing the element's type, ID, classes, and attributes
    pub metaline: MetaLine,
    /// The 0-based index of this element among its siblings
    pub child_index: usize, // 0-based index
}

impl SimpleSelector {
    pub fn builder() -> SimpleSelectorBuilder {
        SimpleSelectorBuilder::default()
    }

    pub fn matches(&self, element: &StackFrame) -> bool {
        // Extract classes from positional args starting with '.'
        let element_classes: HashSet<String> = element
            .metaline
            .arguments
            .args
            .iter()
            .filter_map(|arg| {
                let s = &arg.content;
                s.strip_prefix('.').map(|stripped| stripped.to_string())
            })
            .collect();

        // Helper to check if an attribute exists (as positional or keyword arg)
        let has_attribute = |key: &str| -> bool {
            // Check positional args
            element.metaline.arguments.args.iter().any(|arg| arg.content == key) ||
            // Check keyword args
            element.metaline.arguments.kwargs.iter().any(|(k, _)| k.content == key)
        };

        // Helper to get attribute value (from keyword args)
        let get_attribute = |key: &str| -> Option<String> {
            element
                .metaline
                .arguments
                .kwargs
                .iter()
                .find(|(k, _)| k.content == key)
                .map(|(_, v)| v.content.clone())
        };

        // ID check - IDs are stored in the MetaLine's id field, not as attributes
        if let Some(expected_id) = &self.id {
            if element.metaline.id.value != *expected_id {
                return false;
            }
        }
        // Tag name check
        if let Some(tag_selector) = &self.tag {
            match tag_selector {
                TagSelector::Universal => {
                    // Universal selector matches everything
                }
                TagSelector::TypeName(type_name) => {
                    if type_name != &element.metaline.type_name {
                        return false;
                    }
                }
            }
        }
        // :nth-child check (CSS uses 1-based indexing, our child_index is 0-based)
        if let Some(n) = self.nth_child {
            // nth-child(0) or less should never match
            if n == 0 || n.saturating_sub(1) != element.child_index {
                return false;
            }
        }
        // Class list check
        if !self.classes.is_empty() && !self.classes.is_subset(&element_classes) {
            return false;
        }
        // :not() pseudo-selector check
        if self
            .not_selectors
            .iter()
            .any(|not_selector| not_selector.matches(element))
        {
            return false;
        }
        // Attribute checks
        self.attribute_presence.iter().all(|key| has_attribute(key))
            && self
                .attribute_values
                .iter()
                .all(|(key, val)| get_attribute(key).as_ref() == Some(val))
    }
}

/// Builder pattern for constructing SimpleSelector instances
#[derive(Default)]
pub struct SimpleSelectorBuilder {
    selector: SimpleSelector,
}

impl SimpleSelectorBuilder {
    pub fn id(mut self, id: &str) -> Self {
        self.selector.id = Some(id.to_string());
        self
    }
    pub fn tag(mut self, tag_name: &str) -> Self {
        self.selector.tag = Some(TagSelector::TypeName(tag_name.to_string()));
        self
    }
    pub fn class(mut self, class: &str) -> Self {
        self.selector.classes.insert(class.to_string());
        self
    }
    pub fn attribute(mut self, key: &str) -> Self {
        self.selector.attribute_presence.push(key.to_string());
        self
    }
    pub fn attribute_value(mut self, key: &str, value: &str) -> Self {
        self.selector
            .attribute_values
            .insert(key.to_string(), value.to_string());
        self
    }
    pub fn nth_child(mut self, n: usize) -> Self {
        self.selector.nth_child = Some(n);
        self
    }
    pub fn not(mut self, selector: SimpleSelector) -> Self {
        self.selector.not_selectors.push(selector);
        self
    }
    pub fn build(self) -> SimpleSelector {
        self.selector
    }
    pub fn part(self) -> SelectorPart {
        SelectorPart::Simple(Box::new(self.selector))
    }
}

// ==============================================================================
// Matching Logic
// ==============================================================================

/// Checks if a selector matches against the current document stack
///
/// The stack represents the path from the document root to the current element,
/// similar to how the DOM works in browsers. This function walks backwards through
/// the selector chain, matching each part against the appropriate ancestor in the stack.
///
/// # Arguments
/// * `stack` - The document stack from root to current element
/// * `selector_group` - The selector to match (simplified representation)
///
/// # Returns
/// `true` if the selector matches the current position in the document
pub fn matches_full(stack: &[StackFrame], selector_group: &SimpleSelectorGroup) -> bool {
    let Some((last_selector_part, ancestor_selectors)) = selector_group.selectors.split_last()
    else {
        return false;
    };
    let Some(current_element) = stack.last() else {
        return false;
    };
    let SelectorPart::Simple(last_selector) = last_selector_part else {
        return false;
    };
    if !last_selector.matches(current_element) {
        return false;
    }
    let mut current_stack_idx = stack.len() - 1;
    for chunk in ancestor_selectors.rchunks(2) {
        let (simple_selector, combinator) = match chunk {
            [SelectorPart::Simple(s), SelectorPart::Combinator(c)] => (s, c),
            _ => return false,
        };
        let parent_search_range = 0..current_stack_idx;
        let found_ancestor_idx = match combinator {
            Combinator::Child => parent_search_range
                .last()
                .filter(|&idx| simple_selector.matches(&stack[idx])),
            Combinator::Descendant => parent_search_range
                .rev()
                .find(|&idx| simple_selector.matches(&stack[idx])),
            Combinator::AdjacentSibling | Combinator::GeneralSibling => {
                // Sibling combinators not fully implemented in this demo
                // Would require tracking siblings, not just ancestors
                return false;
            }
        };
        if let Some(idx) = found_ancestor_idx {
            current_stack_idx = idx;
        } else {
            return false;
        }
    }
    true
}

/// Checks if a selector matches against a prefix of the document stack
///
/// Unlike `matches_full`, this function allows additional elements to exist
/// in the stack after the matched portion. This is useful when you want to
/// match a selector against an intermediate element in the document tree.
///
/// # Arguments
/// * `stack` - The document stack from root to current element
/// * `selector_group` - The selector to match (simplified representation)
///
/// # Returns
/// `true` if the selector matches a prefix of the stack (allowing extra elements at the end)
pub fn matches_prefix(stack: &[StackFrame], selector_group: &SimpleSelectorGroup) -> bool {
    // We need at least one selector part
    if selector_group.selectors.is_empty() {
        return false;
    }

    // For prefix matching, we need to find where in the stack the selector starts matching
    // We'll try matching the selector starting from different positions in the stack

    // The selector should be structured as: [selector, combinator, selector, combinator, ..., selector]
    // We need to match from left to right through the selector, finding appropriate elements in the stack

    let selector_parts = &selector_group.selectors;

    // Extract just the simple selectors and their positions
    let mut selectors_with_combinators = Vec::new();
    let mut i = 0;

    while i < selector_parts.len() {
        match &selector_parts[i] {
            SelectorPart::Simple(s) => {
                let combinator = if i + 1 < selector_parts.len() {
                    match &selector_parts[i + 1] {
                        SelectorPart::Combinator(c) => Some(*c),
                        _ => None,
                    }
                } else {
                    None
                };
                selectors_with_combinators.push((s.as_ref(), combinator));
                i += if combinator.is_some() { 2 } else { 1 };
            }
            _ => return false, // Malformed selector
        }
    }

    if selectors_with_combinators.is_empty() {
        return false;
    }

    // Now we need to find a matching sequence in the stack
    // Start from the beginning of the selector and try to match it against the stack

    // We'll iterate through possible starting positions in the stack
    for start_idx in 0..stack.len() {
        if matches_prefix_from_position(stack, &selectors_with_combinators, start_idx) {
            return true;
        }
    }

    false
}

/// Helper function to check if selectors match starting from a specific position in the stack
fn matches_prefix_from_position(
    stack: &[StackFrame],
    selectors: &[(&SimpleSelector, Option<Combinator>)],
    start_idx: usize,
) -> bool {
    if selectors.is_empty() || start_idx >= stack.len() {
        return false;
    }

    let (first_selector, first_combinator) = selectors[0];

    // Check if the first selector matches the element at start_idx
    if !first_selector.matches(&stack[start_idx]) {
        return false;
    }

    // If this is the only selector, we have a match
    if selectors.len() == 1 {
        return true;
    }

    // Otherwise, we need to find the next matching element based on the combinator
    let remaining_selectors = &selectors[1..];

    match first_combinator {
        Some(Combinator::Child) => {
            // For child combinator, the next element must be at start_idx + 1
            if start_idx + 1 < stack.len() {
                matches_prefix_from_position(stack, remaining_selectors, start_idx + 1)
            } else {
                false
            }
        }
        Some(Combinator::Descendant) => {
            // For descendant combinator, try all positions after start_idx
            for next_idx in (start_idx + 1)..stack.len() {
                if matches_prefix_from_position(stack, remaining_selectors, next_idx) {
                    return true;
                }
            }
            false
        }
        Some(Combinator::AdjacentSibling) | Some(Combinator::GeneralSibling) => {
            // Sibling combinators not fully implemented
            false
        }
        None => false, // Should not happen with well-formed selectors
    }
}

#[cfg(test)]
mod tests {
    // use pest::Stack;

    use super::*;

    // ==============================================================================
    // Tests moved from css_selector_demo.rs (applicable to AST)
    // ==============================================================================

    #[test]
    fn test_matches_prefix() {
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 1).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".list", 2).unwrap(),
                child_index: 0,
            },
        ];

        // Selector for `section > paragraph` (child combinator)
        let selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("section").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        // Test that matches_prefix returns true (there's a list after the match)
        assert!(
            matches_prefix(&stack, &selector),
            "Should match section > paragraph prefix even with extra elements"
        );

        // Test that matches_full returns false (because there's extra element)
        assert!(
            !matches_full(&stack, &selector),
            "matches_full should NOT match when there are extra elements"
        );

        // Test with exact match (no extra elements)
        let exact_stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 1).unwrap(),
                child_index: 0,
            },
        ];

        assert!(
            matches_prefix(&exact_stack, &selector),
            "matches_prefix should match exact stack"
        );
        assert!(
            matches_full(&exact_stack, &selector),
            "matches_full should also match exact stack"
        );

        // Test with descendant combinator
        let descendant_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("section").part(),
                SelectorPart::Combinator(Combinator::Descendant),
                SimpleSelector::builder().tag("list").part(),
            ],
        };

        assert!(
            matches_prefix(&stack, &descendant_selector),
            "Should match section list with descendant combinator"
        );
        assert!(
            matches_full(&stack, &descendant_selector),
            "matches_full should also match section list as it's the last element"
        );

        // Test where selector doesn't match at all
        let no_match_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("article").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        assert!(
            !matches_prefix(&stack, &no_match_selector),
            "Should not match non-existent article"
        );
        assert!(
            !matches_full(&stack, &no_match_selector),
            "matches_full should also not match non-existent article"
        );
    }

    #[test]
    fn test_compound_simple_selector() {
        let compound_selector = SimpleSelector::builder()
            .tag("div")
            .id("main")
            .class("red")
            .attribute("data-active")
            .build();

        // A perfect match
        let element_match = StackFrame {
            metaline: MetaLine::parse(".div .. .red .container data-active :main", 0)
                .expect("Failed to parse div"),
            child_index: 0,
        };
        assert!(
            compound_selector.matches(&element_match),
            "Should match when all conditions are met"
        );

        // Fails because of wrong tag
        let mut element_wrong_tag = element_match.clone();
        element_wrong_tag.metaline = MetaLine::parse(".p .. .red .container data-active :main", 0)
            .expect("Failed to parse p");
        assert!(
            !compound_selector.matches(&element_wrong_tag),
            "Should fail on wrong tag"
        );

        // Fails because of missing ID
        let element_no_id = StackFrame {
            metaline: MetaLine::parse(".div .. .red .container data-active", 0)
                .expect("Failed to parse div"),
            child_index: 0,
        };
        assert!(
            !compound_selector.matches(&element_no_id),
            "Should fail on missing id"
        );

        // Fails because of missing class
        let element_no_class = StackFrame {
            metaline: MetaLine::parse(".div .. .container data-active :main", 0)
                .expect("Failed to parse div"),
            child_index: 0,
        };
        assert!(
            !compound_selector.matches(&element_no_class),
            "Should fail on missing class"
        );
    }

    #[test]
    fn test_not_pseudo_selector() {
        // Element to be tested against: <div class="item promoted" id="main">
        let element = StackFrame {
            metaline: MetaLine::parse(".div .. .item .promoted :main", 0).unwrap(),
            child_index: 0,
        };

        // 1. Should match `div:not(.disabled)` because it doesn't have .disabled
        let selector_not_disabled = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::builder().class("disabled").build())
            .build();
        assert!(
            selector_not_disabled.matches(&element),
            "Should match: div:not(.disabled)"
        );

        // 2. Should NOT match `div:not(.promoted)` because it HAS .promoted
        let selector_not_promoted = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::builder().class("promoted").build())
            .build();
        assert!(
            !selector_not_promoted.matches(&element),
            "Should not match: div:not(.promoted)"
        );

        // 3. Should match `.item:not(#other-id)`. It has #main, so it is not #other-id.
        let selector_not_other_id = SimpleSelector::builder()
            .class("item")
            .not(SimpleSelector::builder().id("other-id").build())
            .build();
        assert!(
            selector_not_other_id.matches(&element),
            "Should match: .item:not(#other-id)"
        );

        // 4. Should NOT match `.item:not(#main)` because it HAS the id #main
        let selector_not_main_id = SimpleSelector::builder()
            .class("item")
            .not(SimpleSelector::builder().id("main").build())
            .build();
        assert!(
            !selector_not_main_id.matches(&element),
            "Should not match: .item:not(#main)"
        );

        // 5. Should NOT match because the outer part fails (wrong tag `p`)
        let selector_wrong_tag = SimpleSelector::builder()
            .tag("p")
            .not(SimpleSelector::builder().class("disabled").build())
            .build();
        assert!(
            !selector_wrong_tag.matches(&element),
            "Should not match due to wrong outer tag"
        );

        // 6. Should match because outer part matches, and inner `not` doesn't match the element.
        // Selector: `.item.promoted:not([data-ignore])`
        let selector_with_attr = SimpleSelector::builder()
            .class("item")
            .class("promoted")
            .not(SimpleSelector::builder().attribute("data-ignore").build())
            .build();
        assert!(
            selector_with_attr.matches(&element),
            "Should match complex selector with :not"
        );
    }

    #[test]
    fn test_matches_with_not_in_chain() {
        // Corresponds to: <body> <div class="item"> <p class="content">
        let stack_match = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .item", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".p .. .content", 0).unwrap(),
                child_index: 2,
            },
        ];

        // Corresponds to: <body> <div class="item modal"> <p class="content">
        let stack_no_match = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .item .modal", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".p .. .content", 0).unwrap(),
                child_index: 2,
            },
        ];

        // Selector: `div:not(.modal) > p`
        let selector_group = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder()
                    .tag("div")
                    .not(SimpleSelector::builder().class("modal").build())
                    .part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("p").part(),
            ],
        };

        assert!(
            matches_full(&stack_match, &selector_group),
            "Should match chain with :not on the parent"
        );
        assert!(
            !matches_full(&stack_no_match, &selector_group),
            "Should not match chain because :not(.modal) fails"
        );
    }

    #[test]
    fn test_matches_with_compound_selector_in_chain() {
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).expect("Failed to parse body"),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .wrapper :main", 0)
                    .expect("Failed to parse div"),
                child_index: 0,
            },
        ];

        // Selector: `body > div#main.wrapper`
        let selector = vec![
            SimpleSelector::builder().tag("body").part(),
            SelectorPart::Combinator(Combinator::Child),
            SimpleSelector::builder()
                .tag("div")
                .id("main")
                .class("wrapper")
                .part(),
        ];

        assert!(
            matches_full(
                &stack,
                &SimpleSelectorGroup {
                    selectors: selector
                }
            ),
            "Should match a compound selector in a chain"
        );
    }

    // ==============================================================================
    // New tests for TagSelector
    // ==============================================================================

    #[test]
    fn test_tag_selector_universal() {
        let mut universal = SimpleSelector::builder().build();
        universal.tag = Some(TagSelector::Universal);

        // Universal selector should match any element
        let element1 = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        };
        let element2 = StackFrame {
            metaline: MetaLine::parse(".paragraph", 0).unwrap(),
            child_index: 0,
        };
        let element3 = StackFrame {
            metaline: MetaLine::parse(".custom-element", 0).unwrap(),
            child_index: 0,
        };

        assert!(
            universal.matches(&element1),
            "Universal selector should match div"
        );
        assert!(
            universal.matches(&element2),
            "Universal selector should match paragraph"
        );
        assert!(
            universal.matches(&element3),
            "Universal selector should match custom-element"
        );
    }

    #[test]
    fn test_tag_selector_type_name() {
        let div_selector = SimpleSelector::builder().tag("div").build();
        let para_selector = SimpleSelector::builder().tag("paragraph").build();

        let div_element = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        };
        let para_element = StackFrame {
            metaline: MetaLine::parse(".paragraph", 0).unwrap(),
            child_index: 0,
        };

        assert!(
            div_selector.matches(&div_element),
            "div selector should match div element"
        );
        assert!(
            !div_selector.matches(&para_element),
            "div selector should not match paragraph"
        );
        assert!(
            para_selector.matches(&para_element),
            "paragraph selector should match paragraph"
        );
        assert!(
            !para_selector.matches(&div_element),
            "paragraph selector should not match div"
        );
    }

    // ==============================================================================
    // New tests for AttributeSelector (structure tests, not full matching yet)
    // ==============================================================================

    #[test]
    fn test_attribute_selector_variants() {
        // Test that all AttributeSelector variants can be created and compared
        let presence = AttributeSelector::Presence("disabled".to_string());
        let negated_presence = AttributeSelector::NegatedPresence("disabled".to_string());
        let exact = AttributeSelector::ExactMatch {
            key: "lang".to_string(),
            value: "en".to_string(),
        };
        let negated = AttributeSelector::NegatedMatch {
            key: "lang".to_string(),
            value: "fr".to_string(),
        };
        let prefix = AttributeSelector::PrefixMatch {
            key: "lang".to_string(),
            value: "en".to_string(),
        };
        let suffix = AttributeSelector::SuffixMatch {
            key: "href".to_string(),
            value: ".pdf".to_string(),
        };
        let substring = AttributeSelector::SubstringMatch {
            key: "title".to_string(),
            value: "hello".to_string(),
        };
        let token = AttributeSelector::TokenMatch {
            key: "class".to_string(),
            value: "active".to_string(),
        };
        let starts = AttributeSelector::StartsWith {
            key: "href".to_string(),
            value: "https".to_string(),
        };
        let ends = AttributeSelector::EndsWith {
            key: "href".to_string(),
            value: ".com".to_string(),
        };
        let contains = AttributeSelector::Contains {
            key: "title".to_string(),
            value: "world".to_string(),
        };

        // Test equality
        assert_eq!(
            presence,
            AttributeSelector::Presence("disabled".to_string())
        );
        assert_ne!(presence, negated_presence);
        assert_ne!(exact, negated);

        // Test debug representation exists
        assert!(!format!("{prefix:?}").is_empty());
        assert!(!format!("{suffix:?}").is_empty());
        assert!(!format!("{substring:?}").is_empty());
        assert!(!format!("{token:?}").is_empty());
        assert!(!format!("{starts:?}").is_empty());
        assert!(!format!("{ends:?}").is_empty());
        assert!(!format!("{contains:?}").is_empty());
    }

    // ==============================================================================
    // New tests for PseudoClassSelector
    // ==============================================================================

    #[test]
    fn test_pseudo_class_selector_variants() {
        let first_child = PseudoClassSelector::FirstChild;
        let last_child = PseudoClassSelector::LastChild;
        let nth_child = PseudoClassSelector::NthChild("2n+1".to_string());
        let not_selector = PseudoClassSelector::Not(Box::new(CompoundSelector {
            base: SimpleSelector::builder().class("active").build(),
            parts: vec![],
        }));
        let simple = PseudoClassSelector::Simple("hover".to_string());
        let nth = PseudoClassSelector::Nth("child".to_string(), "3".to_string());

        // Test equality
        assert_eq!(first_child, PseudoClassSelector::FirstChild);
        assert_ne!(first_child, last_child);
        assert_eq!(nth_child, PseudoClassSelector::NthChild("2n+1".to_string()));

        // Test debug representation
        assert!(!format!("{first_child:?}").is_empty());
        assert!(!format!("{last_child:?}").is_empty());
        assert!(!format!("{nth_child:?}").is_empty());
        assert!(!format!("{not_selector:?}").is_empty());
        assert!(!format!("{simple:?}").is_empty());
        assert!(!format!("{nth:?}").is_empty());
    }

    // ==============================================================================
    // New tests for Combinator
    // ==============================================================================

    #[test]
    fn test_combinator_types() {
        let descendant = Combinator::Descendant;
        let child = Combinator::Child;
        let adjacent = Combinator::AdjacentSibling;
        let general = Combinator::GeneralSibling;

        // Test equality and inequality
        assert_eq!(descendant, Combinator::Descendant);
        assert_ne!(descendant, child);
        assert_ne!(child, adjacent);
        assert_ne!(adjacent, general);

        // Test Copy trait
        let copied = descendant;
        assert_eq!(copied, Combinator::Descendant);
    }

    #[test]
    fn test_combinator_in_selector_chain() {
        // Test descendant combinator: "body paragraph"
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 0).unwrap(),
                child_index: 1,
            },
        ];

        let descendant_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("body").part(),
                SelectorPart::Combinator(Combinator::Descendant),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        assert!(
            matches_full(&stack, &descendant_selector),
            "Should match with descendant combinator"
        );

        // Test child combinator: "section > paragraph"
        let child_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("section").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        assert!(
            matches_full(&stack, &child_selector),
            "Should match with child combinator"
        );

        // Test that child combinator fails when not direct child
        let wrong_child_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("body").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        assert!(
            !matches_full(&stack, &wrong_child_selector),
            "Should not match when child combinator used but not direct child"
        );
    }

    // ==============================================================================
    // New tests for SimpleSelectorBuilder
    // ==============================================================================

    #[test]
    fn test_simple_selector_builder_methods() {
        // Test building with all methods
        let selector = SimpleSelector::builder()
            .id("main")
            .tag("div")
            .class("container")
            .class("active")
            .attribute("role")
            .attribute_value("aria-label", "navigation")
            .nth_child(3)
            .not(SimpleSelector::builder().class("disabled").build())
            .build();

        assert_eq!(selector.id, Some("main".to_string()));
        assert_eq!(selector.tag, Some(TagSelector::TypeName("div".to_string())));
        assert!(selector.classes.contains("container"));
        assert!(selector.classes.contains("active"));
        assert_eq!(selector.classes.len(), 2);
        assert!(selector.attribute_presence.contains(&"role".to_string()));
        assert_eq!(
            selector.attribute_values.get("aria-label"),
            Some(&"navigation".to_string())
        );
        assert_eq!(selector.nth_child, Some(3));
        assert_eq!(selector.not_selectors.len(), 1);
    }

    #[test]
    fn test_simple_selector_builder_part() {
        let part = SimpleSelector::builder().tag("div").part();

        match part {
            SelectorPart::Simple(selector) => {
                assert_eq!(selector.tag, Some(TagSelector::TypeName("div".to_string())));
            }
            _ => panic!("Expected SelectorPart::Simple"),
        }
    }

    // ==============================================================================
    // Edge case tests for matching logic
    // ==============================================================================

    #[test]
    fn test_matching_empty_selector() {
        // Empty selector should match any element
        let empty_selector = SimpleSelector::builder().build();

        let element = StackFrame {
            metaline: MetaLine::parse(".div .. .class id=test", 0).unwrap(),
            child_index: 0,
        };

        assert!(
            empty_selector.matches(&element),
            "Empty selector should match any element"
        );
    }

    #[test]
    fn test_matching_multiple_classes() {
        let selector = SimpleSelector::builder()
            .class("red")
            .class("bold")
            .class("large")
            .build();

        // Element with all required classes
        let matching_element = StackFrame {
            metaline: MetaLine::parse(".div .. .red .bold .large .extra", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&matching_element),
            "Should match when all classes present"
        );

        // Element missing one class
        let missing_class = StackFrame {
            metaline: MetaLine::parse(".div .. .red .bold", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            !selector.matches(&missing_class),
            "Should not match when class missing"
        );
    }

    #[test]
    fn test_matching_nth_child_edge_cases() {
        // Test nth-child(1) - first child
        let first_selector = SimpleSelector::builder().nth_child(1).build();

        let first_child = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0, // 0-based, so 0 = first child
        };
        assert!(
            first_selector.matches(&first_child),
            "nth-child(1) should match first child"
        );

        let second_child = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 1,
        };
        assert!(
            !first_selector.matches(&second_child),
            "nth-child(1) should not match second child"
        );
    }

    #[test]
    fn test_matching_attribute_value_with_quotes() {
        let selector = SimpleSelector::builder()
            .attribute_value("title", "Hello World")
            .build();

        let element = StackFrame {
            metaline: MetaLine::parse(r#".div .. title="Hello World""#, 0).unwrap(),
            child_index: 0,
        };

        assert!(
            selector.matches(&element),
            "Should match attribute with spaces"
        );
    }

    #[test]
    fn test_matching_multiple_not_selectors() {
        // Element should not have .disabled OR .hidden
        let selector = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::builder().class("disabled").build())
            .not(SimpleSelector::builder().class("hidden").build())
            .build();

        let visible_element = StackFrame {
            metaline: MetaLine::parse(".div .. .visible", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&visible_element),
            "Should match when neither class present"
        );

        let disabled_element = StackFrame {
            metaline: MetaLine::parse(".div .. .disabled", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            !selector.matches(&disabled_element),
            "Should not match with .disabled"
        );

        let hidden_element = StackFrame {
            metaline: MetaLine::parse(".div .. .hidden", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            !selector.matches(&hidden_element),
            "Should not match with .hidden"
        );
    }

    #[test]
    fn test_empty_stack_matching() {
        let selector_group = SimpleSelectorGroup {
            selectors: vec![SimpleSelector::builder().tag("div").part()],
        };

        let empty_stack: Vec<StackFrame> = vec![];
        assert!(
            !matches_full(&empty_stack, &selector_group),
            "Should not match empty stack"
        );
    }

    #[test]
    fn test_selector_with_only_combinator() {
        // Invalid selector that's just a combinator
        let invalid_selector = SimpleSelectorGroup {
            selectors: vec![SelectorPart::Combinator(Combinator::Child)],
        };

        let stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];

        assert!(
            !matches_full(&stack, &invalid_selector),
            "Should not match invalid selector"
        );
    }

    #[test]
    fn test_complex_nested_not_selector() {
        // Test :not() with compound selector inside
        let selector = SimpleSelector::builder()
            .tag("div")
            .not(
                SimpleSelector::builder()
                    .class("disabled")
                    .attribute("aria-hidden")
                    .build(),
            )
            .build();

        // Element that doesn't match the :not() condition (good)
        let visible_element = StackFrame {
            metaline: MetaLine::parse(".div .. .active", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&visible_element),
            "Should match when :not() condition not met"
        );

        // Element with just the class but not the attribute (still good)
        let partial_match = StackFrame {
            metaline: MetaLine::parse(".div .. .disabled", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&partial_match),
            "Should match - :not() needs both class AND attribute"
        );

        // Element with both class and attribute (should fail)
        let hidden_element = StackFrame {
            metaline: MetaLine::parse(".div .. .disabled aria-hidden=true", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            !selector.matches(&hidden_element),
            "Should not match when :not() condition is met"
        );
    }

    #[test]
    fn test_stack_frame_equality() {
        let frame1 = StackFrame {
            metaline: MetaLine::parse(".div .. .class", 0).unwrap(),
            child_index: 0,
        };
        let frame2 = StackFrame {
            metaline: MetaLine::parse(".div .. .class", 0).unwrap(),
            child_index: 0,
        };
        let frame3 = StackFrame {
            metaline: MetaLine::parse(".div .. .class", 0).unwrap(),
            child_index: 1,
        };

        assert_eq!(frame1, frame2, "Identical frames should be equal");
        assert_ne!(
            frame1, frame3,
            "Different child_index should make frames unequal"
        );
    }

    #[test]
    fn test_compound_selector_structure() {
        let compound = CompoundSelector {
            base: SimpleSelector::builder().tag("div").build(),
            parts: vec![
                (
                    Combinator::Child,
                    SimpleSelector::builder().tag("p").build(),
                ),
                (
                    Combinator::Descendant,
                    SimpleSelector::builder().class("content").build(),
                ),
            ],
        };

        assert_eq!(
            compound.base.tag,
            Some(TagSelector::TypeName("div".to_string()))
        );
        assert_eq!(compound.parts.len(), 2);
        assert_eq!(compound.parts[0].0, Combinator::Child);
        assert_eq!(compound.parts[1].0, Combinator::Descendant);
    }

    #[test]
    fn test_selector_group_structure() {
        let group = SelectorGroup {
            selectors: vec![
                CompoundSelector {
                    base: SimpleSelector::builder().tag("h1").build(),
                    parts: vec![],
                },
                CompoundSelector {
                    base: SimpleSelector::builder().tag("h2").build(),
                    parts: vec![],
                },
            ],
        };

        assert_eq!(group.selectors.len(), 2);
        assert_eq!(
            group.selectors[0].base.tag,
            Some(TagSelector::TypeName("h1".to_string()))
        );
        assert_eq!(
            group.selectors[1].base.tag,
            Some(TagSelector::TypeName("h2".to_string()))
        );
    }

    // ==============================================================================
    // Edge case tests for attribute matching
    // ==============================================================================

    #[test]
    fn test_matching_attribute_as_positional_arg() {
        let selector = SimpleSelector::builder().attribute("disabled").build();

        // Element with 'disabled' as positional argument
        let element_pos = StackFrame {
            metaline: MetaLine::parse(".button .. disabled", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&element_pos),
            "Should match attribute as positional arg"
        );

        // Element with 'disabled' as keyword argument
        let element_kw = StackFrame {
            metaline: MetaLine::parse(".button .. disabled=true", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&element_kw),
            "Should match attribute as keyword arg"
        );
    }

    #[test]
    fn test_matching_id_attribute() {
        let selector = SimpleSelector::builder().id("main-content").build();

        // Test ID specified with Wasot syntax
        let element_id = StackFrame {
            metaline: MetaLine::parse(".div :main-content", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&element_id),
            "Should match id specified with :syntax"
        );

        // Wrong ID
        let element_wrong_id = StackFrame {
            metaline: MetaLine::parse(".div :other-content", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            !selector.matches(&element_wrong_id),
            "Should not match wrong id"
        );

        // No ID (auto-generated IDs are numeric)
        let element_no_id = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        };
        // Auto-generated IDs are numeric, our selector expects "main-content"
        assert!(
            !selector.matches(&element_no_id),
            "Should not match auto-generated id"
        );
    }

    #[test]
    fn test_matching_complex_attribute_values() {
        // Test attribute values with special characters
        let selector = SimpleSelector::builder()
            .attribute_value("data-url", "https://example.com/path?query=1")
            .build();

        let element = StackFrame {
            metaline: MetaLine::parse(r#".link .. data-url="https://example.com/path?query=1""#, 0)
                .unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&element),
            "Should match complex attribute value"
        );
    }

    #[test]
    fn test_matching_class_edge_cases() {
        // Test class matching with special cases
        let selector = SimpleSelector::builder()
            .class("highlight")
            .class("active")
            .build();

        // Element with classes in different order
        let element_reordered = StackFrame {
            metaline: MetaLine::parse(".div .. .active .highlight .extra", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&element_reordered),
            "Should match classes in any order"
        );

        // Element with duplicate classes (shouldn't happen but let's test)
        let element_dup = StackFrame {
            metaline: MetaLine::parse(".div .. .highlight .highlight .active", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            selector.matches(&element_dup),
            "Should match with duplicate classes"
        );

        // Element with class-like positional args (not prefixed with .)
        let element_no_dot = StackFrame {
            metaline: MetaLine::parse(".div .. highlight active", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            !selector.matches(&element_no_dot),
            "Should not match non-class args"
        );
    }

    #[test]
    fn test_matching_nth_child_boundary_cases() {
        // Test nth-child with boundary values
        let selector_zero = SimpleSelector::builder().nth_child(0).build();
        let selector_max = SimpleSelector::builder().nth_child(usize::MAX).build();

        let element_first = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        };

        // nth-child(0) doesn't match any element (CSS is 1-based)
        assert!(
            !selector_zero.matches(&element_first),
            "nth-child(0) should not match first child"
        );

        // nth-child(MAX) won't match unless we have that many siblings
        assert!(
            !selector_max.matches(&element_first),
            "nth-child(MAX) should not match first child"
        );

        // Test with saturating_sub edge case
        let element_large_index = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: usize::MAX - 1,
        };
        assert!(
            selector_max.matches(&element_large_index),
            "Should match when indices align at boundary"
        );
    }

    #[test]
    fn test_matching_empty_collections() {
        // Test selectors with empty collections
        let selector = SimpleSelector {
            id: None,
            tag: None,
            classes: HashSet::new(),
            attributes: vec![],
            pseudo_classes: vec![],
            attribute_presence: vec![],
            attribute_values: HashMap::new(),
            nth_child: None,
            not_selectors: vec![],
        };

        let element = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        };

        assert!(
            selector.matches(&element),
            "Empty selector should match any element"
        );
    }

    #[test]
    fn test_matching_not_selector_edge_cases() {
        // Test :not() with empty selector
        let selector = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::default())
            .build();

        let element = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        };

        // Empty :not() selector matches everything, so :not(empty) matches nothing
        assert!(
            !selector.matches(&element),
            "Should not match when :not() contains empty selector"
        );

        // Test multiple :not() conditions
        let multi_not = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::builder().class("hidden").build())
            .not(SimpleSelector::builder().class("disabled").build())
            .not(SimpleSelector::builder().attribute("aria-hidden").build())
            .build();

        let visible_element = StackFrame {
            metaline: MetaLine::parse(".div .. .visible", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            multi_not.matches(&visible_element),
            "Should match when none of the :not() conditions are met"
        );

        let hidden_element = StackFrame {
            metaline: MetaLine::parse(".div .. .hidden", 0).unwrap(),
            child_index: 0,
        };
        assert!(
            !multi_not.matches(&hidden_element),
            "Should not match when any :not() condition is met"
        );
    }

    #[test]
    fn test_matches_function_edge_cases() {
        // Test matches() with edge case selectors

        // Empty selector group
        let empty_group = SimpleSelectorGroup { selectors: vec![] };
        let stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];
        assert!(
            !matches_full(&stack, &empty_group),
            "Empty selector group should not match"
        );

        // Selector starting with combinator (invalid)
        let invalid_selector = SimpleSelectorGroup {
            selectors: vec![
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("div").part(),
            ],
        };
        assert!(
            !matches_full(&stack, &invalid_selector),
            "Selector starting with combinator should not match"
        );

        // Selector ending with combinator (invalid)
        let trailing_combinator = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("div").part(),
                SelectorPart::Combinator(Combinator::Child),
            ],
        };
        assert!(
            !matches_full(&stack, &trailing_combinator),
            "Selector ending with combinator should not match"
        );

        // Malformed selector with consecutive combinators
        let double_combinator = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("div").part(),
                SelectorPart::Combinator(Combinator::Child),
                SelectorPart::Combinator(Combinator::Descendant),
                SimpleSelector::builder().tag("p").part(),
            ],
        };
        assert!(
            !matches_full(&stack, &double_combinator),
            "Selector with consecutive combinators should not match"
        );
    }

    #[test]
    fn test_matches_deep_nesting() {
        // Test matching with deeply nested structures
        let mut stack = vec![];
        for i in 0..20 {
            stack.push(StackFrame {
                metaline: MetaLine::parse(&format!(".level{i}"), 0).unwrap(),
                child_index: 0,
            });
        }

        // Simple descendant selector
        let shallow_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("level0").part(),
                SelectorPart::Combinator(Combinator::Descendant),
                SimpleSelector::builder().tag("level19").part(),
            ],
        };
        assert!(
            matches_full(&stack, &shallow_selector),
            "Should match deeply nested descendant"
        );

        // Deep child selector chain
        let mut deep_child_parts = vec![];
        for i in 18..20 {
            // Just test the last few levels
            if !deep_child_parts.is_empty() {
                deep_child_parts.push(SelectorPart::Combinator(Combinator::Child));
            }
            deep_child_parts.push(SimpleSelector::builder().tag(&format!("level{i}")).part());
        }
        let deep_child_selector = SimpleSelectorGroup {
            selectors: deep_child_parts,
        };
        assert!(
            matches_full(&stack, &deep_child_selector),
            "Should match deep child chain"
        );
    }

    #[test]
    fn test_stack_frame_with_complex_metaline() {
        // Test StackFrame with complex MetaLine containing many features
        let frame = StackFrame {
            metaline: MetaLine::parse(
                r#".custom-type .. arg1 "arg with spaces" .class1 .class2 key1=value1 key2="value with spaces" disabled :123"#,
                0,
            )
            .unwrap(),
            child_index: 5,
        };

        // Test various selectors against this complex element
        assert!(SimpleSelector::builder()
            .tag("custom-type")
            .build()
            .matches(&frame));
        assert!(SimpleSelector::builder()
            .class("class1")
            .build()
            .matches(&frame));
        assert!(SimpleSelector::builder()
            .class("class2")
            .build()
            .matches(&frame));
        assert!(SimpleSelector::builder()
            .attribute("disabled")
            .build()
            .matches(&frame));
        assert!(SimpleSelector::builder()
            .attribute_value("key1", "value1")
            .build()
            .matches(&frame));
        assert!(SimpleSelector::builder()
            .attribute_value("key2", "value with spaces")
            .build()
            .matches(&frame));
        assert!(SimpleSelector::builder()
            .nth_child(6)
            .build()
            .matches(&frame)); // child_index 5 = nth-child(6)

        // Complex compound selector
        let compound = SimpleSelector::builder()
            .tag("custom-type")
            .class("class1")
            .class("class2")
            .attribute("disabled")
            .attribute_value("key1", "value1")
            .build();
        assert!(
            compound.matches(&frame),
            "Complex compound selector should match"
        );
    }

    #[test]
    fn test_pseudo_class_selector_equality() {
        // Test all PseudoClassSelector variants for equality
        let selectors = vec![
            PseudoClassSelector::FirstChild,
            PseudoClassSelector::LastChild,
            PseudoClassSelector::NthChild("2".to_string()),
            PseudoClassSelector::NthChild("2n+1".to_string()),
            PseudoClassSelector::Simple("hover".to_string()),
            PseudoClassSelector::Simple("focus".to_string()),
            PseudoClassSelector::Nth("child".to_string(), "3".to_string()),
            PseudoClassSelector::Not(Box::new(CompoundSelector {
                base: SimpleSelector::builder().class("active").build(),
                parts: vec![],
            })),
        ];

        // Test self-equality
        for selector in &selectors {
            assert_eq!(selector, selector, "Selector should equal itself");
        }

        // Test inequality between different variants
        assert_ne!(selectors[0], selectors[1]);
        assert_ne!(selectors[2], selectors[3]);
        assert_ne!(selectors[4], selectors[5]);
    }

    #[test]
    fn test_attribute_selector_all_variants() {
        // Create all AttributeSelector variants
        let variants = vec![
            AttributeSelector::Presence("attr".to_string()),
            AttributeSelector::NegatedPresence("attr".to_string()),
            AttributeSelector::ExactMatch {
                key: "attr".to_string(),
                value: "val".to_string(),
            },
            AttributeSelector::NegatedMatch {
                key: "attr".to_string(),
                value: "val".to_string(),
            },
            AttributeSelector::PrefixMatch {
                key: "attr".to_string(),
                value: "pre".to_string(),
            },
            AttributeSelector::SuffixMatch {
                key: "attr".to_string(),
                value: "suf".to_string(),
            },
            AttributeSelector::SubstringMatch {
                key: "attr".to_string(),
                value: "sub".to_string(),
            },
            AttributeSelector::TokenMatch {
                key: "attr".to_string(),
                value: "tok".to_string(),
            },
            AttributeSelector::StartsWith {
                key: "attr".to_string(),
                value: "start".to_string(),
            },
            AttributeSelector::EndsWith {
                key: "attr".to_string(),
                value: "end".to_string(),
            },
            AttributeSelector::Contains {
                key: "attr".to_string(),
                value: "cont".to_string(),
            },
        ];

        // Test that each variant is distinct
        for (i, v1) in variants.iter().enumerate() {
            for (j, v2) in variants.iter().enumerate() {
                if i == j {
                    assert_eq!(v1, v2, "Same variant should be equal");
                } else {
                    assert_ne!(v1, v2, "Different variants should not be equal");
                }
            }
        }

        // Test clone
        for variant in &variants {
            let cloned = variant.clone();
            assert_eq!(variant, &cloned, "Cloned variant should be equal");
        }
    }

    #[test]
    fn test_selector_builder_edge_cases() {
        // Test builder with empty strings
        let selector = SimpleSelector::builder()
            .id("")
            .tag("")
            .class("")
            .attribute("")
            .attribute_value("", "")
            .build();

        assert_eq!(selector.id, Some(String::new()));
        assert_eq!(selector.tag, Some(TagSelector::TypeName(String::new())));
        assert!(selector.classes.contains(""));
        assert!(selector.attribute_presence.contains(&String::new()));
        assert_eq!(selector.attribute_values.get(""), Some(&String::new()));

        // Test builder with very long strings
        let long_str = "a".repeat(1000);
        let long_selector = SimpleSelector::builder()
            .id(&long_str)
            .tag(&long_str)
            .class(&long_str)
            .build();

        assert_eq!(long_selector.id.as_ref().map(|s| s.len()), Some(1000));
        assert!(long_selector.classes.iter().any(|c| c.len() == 1000));
    }

    #[test]
    fn test_combinator_copy_trait() {
        // Verify Copy trait implementation
        let combinators = [
            Combinator::Descendant,
            Combinator::Child,
            Combinator::AdjacentSibling,
            Combinator::GeneralSibling,
        ];

        for &combinator in &combinators {
            let copied = combinator; // This uses Copy
            let cloned = combinator; // This uses Clone (derived from Copy)
            assert_eq!(combinator, copied);
            assert_eq!(combinator, cloned);
        }
    }

    // Tests for uncovered lines in matches_full function
    #[test]
    fn test_matches_full_sibling_combinators() {
        // Test AdjacentSibling and GeneralSibling combinators (lines 325-329)
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 0).unwrap(),
                child_index: 0,
            },
        ];

        // Test AdjacentSibling combinator - should return false (not implemented)
        let adjacent_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("div").part(),
                SelectorPart::Combinator(Combinator::AdjacentSibling),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        assert!(
            !matches_full(&stack, &adjacent_selector),
            "AdjacentSibling should return false (not implemented)"
        );

        // Test GeneralSibling combinator - should return false (not implemented)
        let general_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("div").part(),
                SelectorPart::Combinator(Combinator::GeneralSibling),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        assert!(
            !matches_full(&stack, &general_selector),
            "GeneralSibling should return false (not implemented)"
        );
    }

    #[test]
    fn test_matches_full_no_ancestor_found() {
        // Test case where no ancestor matches (lines 331-337)
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 0).unwrap(),
                child_index: 0,
            },
        ];

        // Selector looking for "article > paragraph" when we have "section > paragraph"
        let no_match_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("article").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("paragraph").part(),
            ],
        };

        assert!(
            !matches_full(&stack, &no_match_selector),
            "Should return false when ancestor not found"
        );
    }

    // Tests for matches_prefix function edge cases
    #[test]
    fn test_matches_prefix_empty_selectors() {
        // Test empty selectors (lines 354-355)
        let stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];

        let empty_selector = SimpleSelectorGroup { selectors: vec![] };

        assert!(
            !matches_prefix(&stack, &empty_selector),
            "Should return false for empty selectors"
        );
    }

    #[test]
    fn test_matches_prefix_malformed_selector() {
        // Test malformed selector that starts with combinator (line 384)
        let stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];

        let malformed_selector = SimpleSelectorGroup {
            selectors: vec![
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("div").part(),
            ],
        };

        assert!(
            !matches_prefix(&stack, &malformed_selector),
            "Should return false for malformed selector"
        );
    }

    #[test]
    fn test_matches_prefix_no_combinators() {
        // Test single selector with no combinator (lines 378-382)
        let stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];

        let single_selector = SimpleSelectorGroup {
            selectors: vec![SimpleSelector::builder().tag("div").part()],
        };

        assert!(
            matches_prefix(&stack, &single_selector),
            "Should match single selector with no combinator"
        );
    }

    #[test]
    fn test_matches_prefix_with_extracted_selectors() {
        // Test the selector extraction logic (lines 367-376)
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".span", 0).unwrap(),
                child_index: 0,
            },
        ];

        let complex_selector = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder().tag("div").part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("span").part(),
            ],
        };

        assert!(
            matches_prefix(&stack, &complex_selector),
            "Should handle selector extraction correctly"
        );
    }

    // Tests for matches_prefix_from_position function
    #[test]
    fn test_matches_prefix_from_position_edge_cases() {
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".span", 0).unwrap(),
                child_index: 0,
            },
        ];

        // Test with empty selectors (lines 411-412)
        let empty_selectors: &[(&SimpleSelector, Option<Combinator>)] = &[];
        assert!(
            !matches_prefix_from_position(&stack, empty_selectors, 0),
            "Should return false for empty selectors"
        );

        // Test with start_idx >= stack.len() (lines 411-412)
        let selector = SimpleSelector::builder().tag("div").build();
        let selectors = &[(&selector, None)];
        assert!(
            !matches_prefix_from_position(&stack, selectors, stack.len()),
            "Should return false when start_idx >= stack.len()"
        );

        // Test when first selector doesn't match (lines 418-419)
        let wrong_selector = SimpleSelector::builder().tag("paragraph").build();
        let wrong_selectors: &[(&SimpleSelector, Option<Combinator>)] = &[(&wrong_selector, None)];
        assert!(
            !matches_prefix_from_position(&stack, wrong_selectors, 0),
            "Should return false when first selector doesn't match"
        );
    }

    #[test]
    fn test_matches_prefix_from_position_single_selector() {
        // Test single selector case (lines 423-424)
        let stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];

        let selector = SimpleSelector::builder().tag("div").build();
        let single_selectors = &[(&selector, None)];

        assert!(
            matches_prefix_from_position(&stack, single_selectors, 0),
            "Should return true for matching single selector"
        );
    }

    #[test]
    fn test_matches_prefix_from_position_child_combinator() {
        // Test child combinator edge cases (lines 433-437)
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".span", 0).unwrap(),
                child_index: 0,
            },
        ];

        let div_selector = SimpleSelector::builder().tag("div").build();
        let span_selector = SimpleSelector::builder().tag("span").build();

        // Test successful child combinator
        let child_selectors = &[
            (&div_selector, Some(Combinator::Child)),
            (&span_selector, None),
        ];
        assert!(
            matches_prefix_from_position(&stack, child_selectors, 0),
            "Should match child combinator when next element exists"
        );

        // Test child combinator at end of stack (lines 435-437)
        let single_element_stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];

        assert!(
            !matches_prefix_from_position(&single_element_stack, child_selectors, 0),
            "Should return false when child combinator at end of stack"
        );
    }

    #[test]
    fn test_matches_prefix_from_position_descendant_combinator() {
        // Test descendant combinator (lines 441-446)
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".span", 0).unwrap(),
                child_index: 0,
            },
        ];

        let div_selector = SimpleSelector::builder().tag("div").build();
        let span_selector = SimpleSelector::builder().tag("span").build();

        let descendant_selectors = &[
            (&div_selector, Some(Combinator::Descendant)),
            (&span_selector, None),
        ];

        assert!(
            matches_prefix_from_position(&stack, descendant_selectors, 0),
            "Should match descendant combinator correctly"
        );

        // Test descendant that doesn't match anything
        let paragraph_selector = SimpleSelector::builder().tag("paragraph").build();
        let no_match_selectors = &[
            (&div_selector, Some(Combinator::Descendant)),
            (&paragraph_selector, None),
        ];

        assert!(
            !matches_prefix_from_position(&stack, no_match_selectors, 0),
            "Should return false when descendant not found"
        );
    }

    #[test]
    fn test_matches_prefix_from_position_sibling_combinators() {
        // Test sibling combinators (lines 448-451)
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".span", 0).unwrap(),
                child_index: 0,
            },
        ];

        let div_selector = SimpleSelector::builder().tag("div").build();
        let span_selector = SimpleSelector::builder().tag("span").build();

        // Test AdjacentSibling (should return false - not implemented)
        let adjacent_selectors = &[
            (&div_selector, Some(Combinator::AdjacentSibling)),
            (&span_selector, None),
        ];
        assert!(
            !matches_prefix_from_position(&stack, adjacent_selectors, 0),
            "AdjacentSibling should return false (not implemented)"
        );

        // Test GeneralSibling (should return false - not implemented)
        let general_selectors = &[
            (&div_selector, Some(Combinator::GeneralSibling)),
            (&span_selector, None),
        ];
        assert!(
            !matches_prefix_from_position(&stack, general_selectors, 0),
            "GeneralSibling should return false (not implemented)"
        );
    }

    #[test]
    fn test_matches_prefix_from_position_none_combinator() {
        // Test None combinator case (line 452)
        let stack = vec![StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        }];

        let div_selector = SimpleSelector::builder().tag("div").build();
        let span_selector = SimpleSelector::builder().tag("span").build();

        let none_combinator_selectors = &[
            (&div_selector, None),
            (&span_selector, None), // This creates malformed selector structure
        ];

        assert!(
            !matches_prefix_from_position(&stack, none_combinator_selectors, 0),
            "Should return false for None combinator with multiple selectors"
        );
    }
}
