//! StackFrameCollector - A helper for managing document stack state during event processing
//!
//! This module provides a utility that encapsulates the common pattern of maintaining
//! a stack of StackFrames while processing document events. It handles the complexity
//! of tracking child indices and provides convenient methods for selector matching.
//!
//! # Example
//!
//! ```
//! use wasot_compiler::parsers::selectors::StackFrameCollector;
//! use wasot_compiler::events::events::Event;
//! use wasot_compiler::parsers::wasot::{MetaLine, ParsedArguments, QuotedString, Id};
//!
//! let mut collector = StackFrameCollector::new(vec![
//!     "section > paragraph".to_string(),
//!     "div.highlight".to_string(),
//! ]);
//!
//! // Create some example events
//! let events = vec![
//!     Event::BlockStarted {
//!         meta_line: MetaLine {
//!             level: 1,
//!             type_name: "section".to_string(),
//!             id: Id::new("".to_string()),
//!             arguments: ParsedArguments { args: vec![], kwargs: vec![] },
//!             inline_text: QuotedString::new("".to_string(), false),
//!             line_index: Some(0),
//!             is_void: false,
//!         }
//!     }
//! ];
//!
//! // Process events
//! for event in events {
//!     collector.handle_event(&event);
//!     
//!     if collector.matches_any_selector() {
//!         // Do something with matching elements
//!     }
//! }
//! ```

use crate::events::events::Event;
use crate::parsers::selectors::{matches_prefix, parse_selector, SimpleSelectorGroup, StackFrame};

/// A utility for managing document stack state and selector matching during event processing.
///
/// This collector encapsulates the common pattern of maintaining a stack of StackFrames
/// while processing document events. It automatically handles:
/// - Building and maintaining the document stack
/// - Tracking child indices for each nesting level
/// - Providing selector matching against the current stack state
/// - Managing stack cleanup when blocks finish
///
/// The collector is designed to be used by consumers that need to match selectors
/// against the current document structure, such as filtering consumers or transformers.
#[derive(Debug)]
pub struct StackFrameCollector {
    /// Parsed selector groups with their original strings for debugging
    selectors: Vec<(String, SimpleSelectorGroup)>,
    /// Stack of block metadata representing the current document path
    current_stack: Vec<StackFrame>,
    /// Track child index for the next block at each nesting level
    next_child_indices: Vec<usize>,
}

impl StackFrameCollector {
    /// Creates a new StackFrameCollector with the given selector strings.
    ///
    /// # Arguments
    /// * `selector_strings` - Vector of CSS-like selector strings to parse and use for matching
    ///
    /// # Returns
    /// A new StackFrameCollector instance. Invalid selectors are logged and ignored.
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec![
    ///     "section paragraph".to_string(),
    ///     "div:nth-child(2)".to_string(),
    /// ]);
    /// ```
    pub fn new(selector_strings: Vec<String>) -> Self {
        let mut selectors = Vec::new();

        // Parse all selector strings into ASTs
        for selector_str in &selector_strings {
            match parse_selector(selector_str) {
                Ok(selector_group) => selectors.push((selector_str.clone(), selector_group)),
                Err(e) => {
                    eprintln!("Failed to parse selector '{selector_str}': {e}");
                }
            }
        }

        if selectors.is_empty() && !selector_strings.is_empty() {
            eprintln!("Warning: No valid selectors were parsed");
        }

        Self {
            selectors,
            current_stack: Vec::new(),
            next_child_indices: vec![0], // Start with index 0 for root level
        }
    }

    /// Processes a document event, updating the internal stack state.
    ///
    /// This method should be called for each event in the document stream.
    /// It automatically handles:
    /// - Adding new blocks to the stack with proper child indices
    /// - Removing blocks when they finish
    /// - Maintaining child index counters for each nesting level
    ///
    /// # Arguments
    /// * `event` - The document event to process
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    /// use wasot_compiler::events::events::Event;
    /// use wasot_compiler::parsers::wasot::{MetaLine, ParsedArguments, QuotedString, Id};
    ///
    /// let mut collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// let event_stream = vec![
    ///     Event::BlockStarted {
    ///         meta_line: MetaLine {
    ///             level: 1,
    ///             type_name: "section".to_string(),
    ///             id: Id::new("".to_string()),
    ///             arguments: ParsedArguments { args: vec![], kwargs: vec![] },
    ///             inline_text: QuotedString::new("".to_string(), false),
    ///             line_index: Some(0),
    ///             is_void: false,
    ///         }
    ///     }
    /// ];
    ///
    /// for event in event_stream {
    ///     collector.handle_event(&event);
    ///     // Check for matches after each event
    ///     if collector.matches_any_selector() {
    ///         // Process matching content
    ///     }
    /// }
    /// ```
    pub fn handle_event(&mut self, event: &Event) {
        match event {
            Event::BlockStarted { meta_line } => {
                // Get the child index for this block
                let level = self.current_stack.len();
                let child_index = if level < self.next_child_indices.len() {
                    self.next_child_indices[level]
                } else {
                    0
                };

                // Add the block to our stack
                self.current_stack.push(StackFrame {
                    metaline: meta_line.clone(),
                    child_index,
                });

                // Update child indices tracking
                if level < self.next_child_indices.len() {
                    self.next_child_indices[level] += 1;
                } else {
                    self.next_child_indices.push(1);
                }
                // Reset child index for the next level
                if level + 1 < self.next_child_indices.len() {
                    self.next_child_indices[level + 1] = 0;
                } else {
                    self.next_child_indices.push(0);
                }
            }
            Event::BlockFinished => {
                // Pop the last block from our stack
                if self.current_stack.pop().is_some() {
                    // Reset child index tracking for this level
                    let level = self.current_stack.len();
                    if level + 1 < self.next_child_indices.len() {
                        self.next_child_indices[level + 1] = 0;
                    }
                }
            }
            Event::TextFinished { .. } => {
                // Text events don't affect the stack structure
            }
            Event::ContextLine { .. } => {
                // Context lines don't affect the stack structure
            }
            Event::VoidElement { .. } => {
                // Void elements are self-closing and don't affect the stack structure
                // They don't push/pop the block stack
            }
        }
    }

    /// Checks if any of the configured selectors match the current stack state.
    ///
    /// This uses prefix matching, which means selectors can match even if there
    /// are additional nested elements below the matched portion.
    ///
    /// # Returns
    /// `true` if any selector matches the current document position, `false` otherwise
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// if collector.matches_any_selector() {
    ///     println!("Current element matches one of our selectors!");
    /// }
    /// ```
    pub fn matches_any_selector(&self) -> bool {
        for (_, selector_group) in &self.selectors {
            if matches_prefix(&self.current_stack, selector_group) {
                return true;
            }
        }
        false
    }

    /// Checks if a specific selector (by index) matches the current stack state.
    ///
    /// # Arguments
    /// * `selector_index` - Index of the selector to check (0-based)
    ///
    /// # Returns
    /// `true` if the specified selector matches, `false` if it doesn't match or index is invalid
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// if collector.matches_selector(0) {
    ///     println!("First selector matches!");
    /// }
    /// ```
    pub fn matches_selector(&self, selector_index: usize) -> bool {
        if let Some((_, selector_group)) = self.selectors.get(selector_index) {
            matches_prefix(&self.current_stack, selector_group)
        } else {
            false
        }
    }

    /// Returns a reference to the current document stack.
    ///
    /// The stack represents the path from the document root to the current element,
    /// with each StackFrame containing the element's metadata and child index.
    ///
    /// # Returns
    /// A slice of StackFrame representing the current document path
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// let stack = collector.current_stack();
    /// println!("Currently nested {} levels deep", stack.len());
    /// if let Some(current) = stack.last() {
    ///     println!("Current element: {}", current.metaline.type_name);
    /// }
    /// ```
    pub fn current_stack(&self) -> &[StackFrame] {
        &self.current_stack
    }

    /// Returns the current nesting depth (number of open blocks).
    ///
    /// # Returns
    /// The number of blocks currently on the stack (0 for root level)
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// println!("Current depth: {}", collector.depth());
    /// ```
    pub fn depth(&self) -> usize {
        self.current_stack.len()
    }

    /// Returns a reference to the currently active element, if any.
    ///
    /// # Returns
    /// `Some(&StackFrame)` for the current element, or `None` if at root level
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// if let Some(current) = collector.current_element() {
    ///     println!("In element: {} (child {})",
    ///              current.metaline.type_name,
    ///              current.child_index);
    /// }
    /// ```
    pub fn current_element(&self) -> Option<&StackFrame> {
        self.current_stack.last()
    }

    /// Returns the number of configured selectors.
    ///
    /// # Returns
    /// The number of selectors that were successfully parsed and are being tracked
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// println!("Tracking {} selectors", collector.selector_count());
    /// ```
    pub fn selector_count(&self) -> usize {
        self.selectors.len()
    }

    /// Returns the selector string at the given index.
    ///
    /// # Arguments
    /// * `index` - Index of the selector (0-based)
    ///
    /// # Returns
    /// `Some(&str)` with the selector string, or `None` if index is invalid
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// for i in 0..collector.selector_count() {
    ///     if let Some(selector) = collector.selector_string(i) {
    ///         println!("Selector {}: {}", i, selector);
    ///     }
    /// }
    /// ```
    pub fn selector_string(&self, index: usize) -> Option<&str> {
        self.selectors.get(index).map(|(s, _)| s.as_str())
    }

    /// Returns all selector strings that currently match.
    ///
    /// # Returns
    /// Vector of selector strings that match the current stack state
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// let matching = collector.matching_selectors();
    /// if !matching.is_empty() {
    ///     println!("Matching selectors: {:?}", matching);
    /// }
    /// ```
    pub fn matching_selectors(&self) -> Vec<&str> {
        self.selectors
            .iter()
            .filter(|(_, selector_group)| matches_prefix(&self.current_stack, selector_group))
            .map(|(s, _)| s.as_str())
            .collect()
    }

    /// Resets the collector to its initial state.
    ///
    /// This clears the stack and resets child index tracking, as if starting
    /// a new document. The configured selectors are preserved.
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let mut collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// collector.reset();
    /// // Now ready to process a new document
    /// ```
    pub fn reset(&mut self) {
        self.current_stack.clear();
        self.next_child_indices = vec![0];
    }

    /// Checks if the collector is currently at the document root (no open blocks).
    ///
    /// # Returns
    /// `true` if no blocks are currently open, `false` otherwise
    ///
    /// # Example
    /// ```
    /// use wasot_compiler::parsers::selectors::StackFrameCollector;
    ///
    /// let collector = StackFrameCollector::new(vec!["section".to_string()]);
    /// if collector.is_at_root() {
    ///     println!("Back at document root");
    /// }
    /// ```
    pub fn is_at_root(&self) -> bool {
        self.current_stack.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parsers::wasot::{Id, MetaLine, ParsedArguments, QuotedString};

    fn create_test_event(type_name: &str, level: usize, line_index: usize) -> Event {
        Event::BlockStarted {
            meta_line: MetaLine {
                level,
                type_name: type_name.to_string(),
                id: Id::new("".to_string()),
                arguments: ParsedArguments {
                    args: vec![],
                    kwargs: vec![],
                },
                inline_text: QuotedString::new("".to_string(), false),
                line_index: Some(line_index),
                is_void: false,
            },
        }
    }

    #[test]
    fn test_basic_stack_management() {
        let mut collector = StackFrameCollector::new(vec![]);

        // Start with empty stack
        assert!(collector.is_at_root());
        assert_eq!(collector.depth(), 0);
        assert!(collector.current_element().is_none());

        // Add a block
        collector.handle_event(&create_test_event("section", 1, 0));
        assert!(!collector.is_at_root());
        assert_eq!(collector.depth(), 1);
        assert!(collector.current_element().is_some());
        assert_eq!(
            collector.current_element().unwrap().metaline.type_name,
            "section"
        );
        assert_eq!(collector.current_element().unwrap().child_index, 0);

        // Add nested block
        collector.handle_event(&create_test_event("paragraph", 2, 1));
        assert_eq!(collector.depth(), 2);
        assert_eq!(
            collector.current_element().unwrap().metaline.type_name,
            "paragraph"
        );
        assert_eq!(collector.current_element().unwrap().child_index, 0);

        // Add sibling block
        collector.handle_event(&Event::BlockFinished);
        collector.handle_event(&create_test_event("paragraph", 2, 2));
        assert_eq!(collector.depth(), 2);
        assert_eq!(collector.current_element().unwrap().child_index, 1); // Second child

        // Close all blocks
        collector.handle_event(&Event::BlockFinished); // paragraph
        collector.handle_event(&Event::BlockFinished); // section
        assert!(collector.is_at_root());
    }

    #[test]
    fn test_selector_parsing_and_counting() {
        let collector = StackFrameCollector::new(vec![
            "section paragraph".to_string(),
            "div.highlight".to_string(),
            "invalid > >".to_string(), // This should fail to parse
        ]);

        assert_eq!(collector.selector_count(), 2); // Only 2 valid selectors
        assert_eq!(collector.selector_string(0), Some("section paragraph"));
        assert_eq!(collector.selector_string(1), Some("div.highlight"));
        assert_eq!(collector.selector_string(2), None); // Invalid index
    }

    #[test]
    fn test_selector_matching() {
        let mut collector = StackFrameCollector::new(vec![
            "section paragraph".to_string(),
            "div:nth-child(2)".to_string(),
        ]);

        // Start with section
        collector.handle_event(&create_test_event("section", 1, 0));
        assert!(!collector.matches_any_selector());
        assert!(!collector.matches_selector(0));
        assert!(!collector.matches_selector(1));

        // Add paragraph (should match first selector)
        collector.handle_event(&create_test_event("paragraph", 2, 1));
        assert!(collector.matches_any_selector());
        assert!(collector.matches_selector(0));
        assert!(!collector.matches_selector(1));

        let matching = collector.matching_selectors();
        assert_eq!(matching, vec!["section paragraph"]);
    }

    #[test]
    fn test_nth_child_matching() {
        let mut collector = StackFrameCollector::new(vec!["div:nth-child(2)".to_string()]);

        // First div (child index 0, nth-child 1)
        collector.handle_event(&create_test_event("div", 1, 0));
        assert!(!collector.matches_any_selector());

        collector.handle_event(&Event::BlockFinished);

        // Second div (child index 1, nth-child 2)
        collector.handle_event(&create_test_event("div", 1, 1));
        assert!(collector.matches_any_selector());
        assert!(collector.matches_selector(0));
    }

    #[test]
    fn test_reset_functionality() {
        let mut collector = StackFrameCollector::new(vec!["section".to_string()]);

        // Build up some state
        collector.handle_event(&create_test_event("section", 1, 0));
        collector.handle_event(&create_test_event("paragraph", 2, 1));
        assert_eq!(collector.depth(), 2);
        assert!(collector.matches_any_selector());

        // Reset should clear everything
        collector.reset();
        assert!(collector.is_at_root());
        assert_eq!(collector.depth(), 0);
        assert!(!collector.matches_any_selector());
        assert_eq!(collector.selector_count(), 1); // Selectors preserved
    }

    #[test]
    fn test_text_events_ignored() {
        let mut collector = StackFrameCollector::new(vec![]);

        collector.handle_event(&create_test_event("section", 1, 0));
        let depth_before = collector.depth();

        // Text events should not affect stack
        collector.handle_event(&Event::TextFinished {
            lines: vec!["Some text".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        });

        assert_eq!(collector.depth(), depth_before);
    }

    #[test]
    fn test_deep_nesting_child_indices() {
        let mut collector = StackFrameCollector::new(vec![]);

        // Create deeply nested structure with siblings
        collector.handle_event(&create_test_event("root", 1, 0));

        // First child of root
        collector.handle_event(&create_test_event("child1", 2, 1));
        assert_eq!(collector.current_element().unwrap().child_index, 0);
        collector.handle_event(&Event::BlockFinished);

        // Second child of root
        collector.handle_event(&create_test_event("child2", 2, 2));
        assert_eq!(collector.current_element().unwrap().child_index, 1);

        // First grandchild
        collector.handle_event(&create_test_event("grandchild1", 3, 3));
        assert_eq!(collector.current_element().unwrap().child_index, 0);
        collector.handle_event(&Event::BlockFinished);

        // Second grandchild
        collector.handle_event(&create_test_event("grandchild2", 3, 4));
        assert_eq!(collector.current_element().unwrap().child_index, 1);
    }

    #[test]
    fn test_empty_selector_list() {
        let collector = StackFrameCollector::new(vec![]);
        assert_eq!(collector.selector_count(), 0);
        assert!(!collector.matches_any_selector());
        assert_eq!(collector.matching_selectors(), Vec::<&str>::new());
    }

    #[test]
    fn test_invalid_selector_handling() {
        let collector = StackFrameCollector::new(vec![
            ">".to_string(), // Invalid
            "valid".to_string(),
            "div > >".to_string(), // Invalid
        ]);

        // Should only have 1 valid selector
        assert_eq!(collector.selector_count(), 1);
        assert_eq!(collector.selector_string(0), Some("valid"));
    }

    #[test]
    fn test_complex_selector_matching() {
        let mut collector =
            StackFrameCollector::new(vec!["document section article paragraph".to_string()]);

        // Build the exact structure
        let types = ["document", "section", "article", "paragraph"];
        for (i, type_name) in types.iter().enumerate() {
            collector.handle_event(&create_test_event(type_name, i + 1, i));
        }

        // Should match the full selector
        assert!(collector.matches_any_selector());
        assert!(collector.matches_selector(0));

        // Add extra nesting - should still match due to prefix matching
        collector.handle_event(&create_test_event("span", 5, 4));
        assert!(collector.matches_any_selector());
    }

    #[test]
    fn test_stack_frame_access() {
        let mut collector = StackFrameCollector::new(vec![]);

        collector.handle_event(&create_test_event("root", 1, 0));
        collector.handle_event(&create_test_event("child", 2, 1));

        let stack = collector.current_stack();
        assert_eq!(stack.len(), 2);
        assert_eq!(stack[0].metaline.type_name, "root");
        assert_eq!(stack[1].metaline.type_name, "child");
        assert_eq!(stack[0].child_index, 0);
        assert_eq!(stack[1].child_index, 0);
    }

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

    #[test]
    fn test_new_with_selectors() {
        // Test lines 85-86 - new function with selectors
        let collector = StackFrameCollector::new(vec!["div".to_string(), "p".to_string()]);
        assert_eq!(collector.selectors.len(), 2);
        assert_eq!(collector.current_stack.len(), 0);
        assert_eq!(collector.next_child_indices, vec![0]);
    }

    #[test]
    fn test_new_selector_parsing() {
        // Test lines 89-93 - selector parsing with errors
        let collector = StackFrameCollector::new(vec![
            "valid".to_string(),
            ">>invalid<<".to_string(), // This should fail to parse and trigger eprintln on line 93
        ]);

        // Only the valid selector should be stored
        assert_eq!(collector.selector_count(), 1);
        assert_eq!(collector.selector_string(0), Some("valid"));
    }

    #[test]
    fn test_new_empty_selectors_warning() {
        // Test lines 98-99 - warning when no valid selectors
        let collector = StackFrameCollector::new(vec![
            ">>>invalid<<<".to_string(),
            "<<<also invalid>>>".to_string(),
        ]);

        // This should trigger the eprintln warning on line 99
        assert_eq!(collector.selector_count(), 0);
        assert!(collector.selectors.is_empty());
    }

    #[test]
    fn test_new_initialization() {
        // Test lines 104-105 - struct initialization
        let collector = StackFrameCollector::new(vec![]);
        assert!(collector.current_stack.is_empty());
        assert_eq!(collector.next_child_indices, vec![0]);
    }

    #[test]
    fn test_handle_event_block_started_existing_level() {
        // Test lines 148-150, 152-154, 156 - handle BlockStarted with existing level
        let mut collector = StackFrameCollector::new(vec![]);

        // Add first block to establish level 0
        collector.handle_event(&create_test_event("first", 1, 0));
        assert_eq!(collector.current_stack.len(), 1);
        assert_eq!(collector.current_stack[0].child_index, 0);

        // Close it and add second block at same level - should increment child index
        collector.handle_event(&Event::BlockFinished);
        collector.handle_event(&create_test_event("second", 1, 1));
        assert_eq!(collector.current_stack.len(), 1);
        assert_eq!(collector.current_stack[0].child_index, 1); // Second child at level 0
    }

    #[test]
    fn test_handle_event_child_index_logic() {
        // Test lines 160-162, 166-167 - child index handling
        let mut collector = StackFrameCollector::new(vec![]);

        // Add first block
        collector.handle_event(&create_test_event("parent", 1, 0));
        assert_eq!(collector.current_stack.len(), 1);
        assert_eq!(collector.current_stack[0].child_index, 0);
        assert_eq!(collector.next_child_indices[0], 1); // Next child at this level would be 1

        // Add nested block
        collector.handle_event(&create_test_event("child", 2, 1));
        assert_eq!(collector.current_stack.len(), 2);
        assert_eq!(collector.current_stack[1].child_index, 0);
        assert_eq!(collector.next_child_indices[1], 1); // Next child at this level would be 1
    }

    #[test]
    fn test_handle_event_new_level_push() {
        // Test line 169, lines 172-173, line 175 - pushing new levels in next_child_indices
        let mut collector = StackFrameCollector::new(vec![]);

        // Start with 1 level tracked (root)
        assert_eq!(collector.next_child_indices.len(), 1);

        // Add first level block - should push level 1 tracking
        collector.handle_event(&create_test_event("level1", 1, 0));
        assert_eq!(collector.next_child_indices.len(), 2); // [1, 0] - next sibling would be 1, first child is 0

        // Add second level block - should push level 2 tracking
        collector.handle_event(&create_test_event("level2", 2, 1));
        assert_eq!(collector.next_child_indices.len(), 3); // [1, 1, 0] - sibling counts + new child level
    }

    #[test]
    fn test_handle_event_block_finished_with_stack() {
        // Test line 180, lines 182-184 - BlockFinished with items on stack
        let mut collector = StackFrameCollector::new(vec![]);

        // Build nested structure
        collector.handle_event(&create_test_event("parent", 1, 0));
        collector.handle_event(&create_test_event("child", 2, 1));
        assert_eq!(collector.current_stack.len(), 2);

        // Pop child - should reset child index tracking for that level
        collector.handle_event(&Event::BlockFinished);
        assert_eq!(collector.current_stack.len(), 1);
        // Should have reset the tracking for the child level
    }

    #[test]
    fn test_matches_any_selector_detailed() {
        // Test lines 211-214, line 217 - matches_any_selector logic
        let mut collector =
            StackFrameCollector::new(vec!["section".to_string(), "div.highlight".to_string()]);

        // No match initially
        assert!(!collector.matches_any_selector());

        // Add section - should match first selector
        collector.handle_event(&create_test_event("section", 1, 0));
        assert!(collector.matches_any_selector());
    }

    #[test]
    fn test_matches_selector_detailed() {
        // Test lines 237-239, line 241 - matches_selector with bounds checking
        let mut collector = StackFrameCollector::new(vec!["section".to_string()]);

        // Invalid index - should return false (line 241)
        assert!(!collector.matches_selector(1));
        assert!(!collector.matches_selector(999));

        // Valid index, no match
        assert!(!collector.matches_selector(0));

        // Valid index, with match (lines 237-239)
        collector.handle_event(&create_test_event("section", 1, 0));
        assert!(collector.matches_selector(0));
    }

    #[test]
    fn test_current_stack_access() {
        // Test lines 264-265 - current_stack method
        let mut collector = StackFrameCollector::new(vec![]);

        // Empty stack
        let stack = collector.current_stack();
        assert_eq!(stack.len(), 0);

        // With elements
        collector.handle_event(&create_test_event("test", 1, 0));
        let stack = collector.current_stack();
        assert_eq!(stack.len(), 1);
        assert_eq!(stack[0].metaline.type_name, "test");
    }

    #[test]
    fn test_depth_method() {
        // Test lines 280-281 - depth method
        let mut collector = StackFrameCollector::new(vec![]);

        // Start at depth 0
        assert_eq!(collector.depth(), 0);

        // Add blocks and check depth
        collector.handle_event(&create_test_event("level1", 1, 0));
        assert_eq!(collector.depth(), 1);

        collector.handle_event(&create_test_event("level2", 2, 1));
        assert_eq!(collector.depth(), 2);

        // Remove block
        collector.handle_event(&Event::BlockFinished);
        assert_eq!(collector.depth(), 1);
    }

    #[test]
    fn test_current_element_method() {
        // Test lines 300-301 - current_element method
        let mut collector = StackFrameCollector::new(vec![]);

        // No current element at root
        assert!(collector.current_element().is_none());

        // With element
        collector.handle_event(&create_test_event("test", 1, 0));
        let current = collector.current_element();
        assert!(current.is_some());
        assert_eq!(current.unwrap().metaline.type_name, "test");
    }

    #[test]
    fn test_selector_count_method() {
        // Test lines 316-317 - selector_count method
        let collector1 = StackFrameCollector::new(vec![]);
        assert_eq!(collector1.selector_count(), 0);

        let collector2 = StackFrameCollector::new(vec!["a".to_string(), "b".to_string()]);
        assert_eq!(collector2.selector_count(), 2);
    }

    #[test]
    fn test_selector_string_method() {
        // Test lines 339-340 - selector_string method
        let collector = StackFrameCollector::new(vec!["first".to_string(), "second".to_string()]);

        // Valid indices
        assert_eq!(collector.selector_string(0), Some("first"));
        assert_eq!(collector.selector_string(1), Some("second"));

        // Invalid index
        assert_eq!(collector.selector_string(2), None);
        assert_eq!(collector.selector_string(999), None);
    }

    #[test]
    fn test_matching_selectors_method() {
        // Test lines 358-359, 361-362 - matching_selectors method
        let mut collector = StackFrameCollector::new(vec![
            "section".to_string(),
            "div".to_string(),
            "paragraph".to_string(),
        ]);

        // No matches initially
        let matches = collector.matching_selectors();
        assert_eq!(matches, Vec::<&str>::new());

        // Add section - should match first selector
        collector.handle_event(&create_test_event("section", 1, 0));
        let matches = collector.matching_selectors();
        assert_eq!(matches, vec!["section"]);

        // Structure that doesn't match multiple selectors
        collector.reset();
        collector.handle_event(&create_test_event("article", 1, 0));
        let matches = collector.matching_selectors();
        assert_eq!(matches, Vec::<&str>::new());
    }

    #[test]
    fn test_reset_method() {
        // Test lines 379-381 - reset method
        let mut collector = StackFrameCollector::new(vec!["section".to_string()]);

        // Build some state
        collector.handle_event(&create_test_event("section", 1, 0));
        collector.handle_event(&create_test_event("child", 2, 1));
        assert_eq!(collector.depth(), 2);
        assert!(collector.matches_any_selector());

        // Reset should clear stack and reset indices
        collector.reset();
        assert_eq!(collector.current_stack.len(), 0);
        assert_eq!(collector.next_child_indices, vec![0]);
        assert_eq!(collector.depth(), 0);
        assert!(!collector.matches_any_selector());

        // But selectors should be preserved
        assert_eq!(collector.selector_count(), 1);
    }

    #[test]
    fn test_is_at_root_method() {
        // Test lines 398-399 - is_at_root method
        let mut collector = StackFrameCollector::new(vec![]);

        // Start at root
        assert!(collector.is_at_root());

        // Add element - not at root
        collector.handle_event(&create_test_event("test", 1, 0));
        assert!(!collector.is_at_root());

        // Remove element - back at root
        collector.handle_event(&Event::BlockFinished);
        assert!(collector.is_at_root());
    }

    #[test]
    fn test_edge_cases() {
        // Test additional edge cases that might hit uncovered lines
        let mut collector = StackFrameCollector::new(vec![]);

        // Test BlockFinished with empty stack (should not crash)
        collector.handle_event(&Event::BlockFinished);
        assert!(collector.is_at_root());

        // Test very deep nesting to exercise index tracking
        for i in 0..10 {
            collector.handle_event(&create_test_event(&format!("level{i}"), i + 1, i));
        }
        assert_eq!(collector.depth(), 10);

        // Test unwinding
        for _ in 0..10 {
            collector.handle_event(&Event::BlockFinished);
        }
        assert!(collector.is_at_root());
    }

    #[test]
    fn test_child_index_edge_cases() {
        // Test edge cases for child index tracking that might hit uncovered branches
        let mut collector = StackFrameCollector::new(vec![]);

        // Create scenario where level >= next_child_indices.len() (might hit line 156)
        collector.handle_event(&create_test_event("test", 1, 0));

        // Manually manipulate state to test edge cases
        collector.next_child_indices.clear(); // This might trigger the else branch on line 156
        collector.handle_event(&create_test_event("test2", 1, 1));

        // Should handle gracefully
        assert_eq!(collector.depth(), 2);
    }
}
