use super::block::{Block, BlockContainer};
use super::parser::{is_meta_line, Id, MetaLine};
use std::sync::{Arc, Mutex};

/// Represents the parsing state for hierarchical elements (blocks and text)
pub struct ElementParser {
    /// Stack of currently open blocks (hierarchical nesting)
    block_stack: Vec<Block>,
    /// Container to store all completed blocks (only used in non-streaming mode)
    container: BlockContainer,
    /// Current implicit text block for collecting non-meta lines
    current_text_block: Option<Block>,
    /// Current line index being processed
    current_line_index: usize,
    /// Next auto-generated ID to use
    next_auto_id: Option<Arc<Mutex<u64>>>,
    /// When true, blocks are not accumulated in container (for memory-efficient streaming)
    streaming_mode: bool,
}

impl ElementParser {
    /// Create a new element parser
    #[must_use]
    pub fn new() -> Self {
        ElementParser {
            block_stack: Vec::new(),
            container: BlockContainer::new(),
            current_text_block: None,
            current_line_index: 0,
            next_auto_id: None,
            streaming_mode: false,
        }
    }

    /// Create a new element parser in streaming mode (memory-efficient, doesn't accumulate blocks)
    ///
    /// In streaming mode, completed blocks are NOT stored in the container. Use this mode
    /// when processing large files where you only need events (e.g., `WasotFileEmitter`).
    #[must_use]
    pub fn new_streaming() -> Self {
        ElementParser {
            block_stack: Vec::new(),
            container: BlockContainer::new(),
            current_text_block: None,
            current_line_index: 0,
            next_auto_id: None,
            streaming_mode: true,
        }
    }

    /// Create a new element parser with auto-ID generation
    #[must_use]
    pub fn new_with_auto_id(next_auto_id: Arc<Mutex<u64>>) -> Self {
        ElementParser {
            block_stack: Vec::new(),
            container: BlockContainer::new(),
            current_text_block: None,
            current_line_index: 0,
            next_auto_id: Some(next_auto_id),
            streaming_mode: false,
        }
    }

    /// Get the current depth of the block stack
    #[must_use]
    pub fn current_depth(&self) -> usize {
        self.block_stack.len()
    }

    /// Process a single line of input
    /// Returns the number of blocks that were closed during processing
    pub fn process_line(&mut self, line: &str, line_index: usize) -> Result<usize, String> {
        debug_assert!(
            line_index >= self.current_line_index,
            "Line indices should be monotonically increasing"
        );
        self.current_line_index = line_index;
        if is_meta_line(line) {
            debug_assert!(line.starts_with('.'), "Meta line should start with dot");
            self.process_meta_line(line)
        } else {
            self.process_content_line(line);
            Ok(0)
        }
    }

    /// Process a meta-line (starts with '.')
    fn process_meta_line(&mut self, line: &str) -> Result<usize, String> {
        debug_assert!(line.starts_with('.'), "Meta line should start with dot");
        debug_assert!(!line.is_empty(), "Meta line should not be empty");

        // Finish any current text block
        self.finish_current_text_block();

        // Parse the meta-line
        let mut meta_line = MetaLine::parse(line, self.current_line_index)
            .ok_or_else(|| format!("Failed to parse meta-line: {line}"))?;

        // Auto-generate ID if needed (but not for close blocks or implicit text blocks)
        if meta_line.id.is_empty() && meta_line.type_name != "/" && meta_line.type_name != "text" {
            if let Some(ref next_id_mutex) = self.next_auto_id {
                if let Ok(mut next_id) = next_id_mutex.lock() {
                    meta_line.id = Id::from_u64(*next_id);
                    *next_id += 1;
                }
            }
        }

        debug_assert!(
            meta_line.level <= 10,
            "Meta line level should be reasonable"
        );

        // Handle explicit block closing
        if meta_line.type_name == "/" {
            self.close_current_block()?;
            return Ok(1); // One block was closed
        }

        // Context lines (type_name starts with '/') don't affect block structure
        // They use syntax: ./mytag or ../mytag
        if meta_line.type_name.starts_with('/') {
            return Ok(0); // No blocks closed
        }

        // Check if this is a heading block
        let is_heading = meta_line.type_name.is_empty(); // Headings have empty type_name

        // Check if this is a void element (self-closing, no children)
        let is_void = meta_line.is_void;

        let block = if is_heading {
            Block::new_heading(meta_line)
        } else {
            Block::new(meta_line)
        };

        // Determine how to handle this block based on its level
        self.handle_block_by_level(block, is_heading, is_void)
    }

    /// Handle a block based on its nesting level
    fn handle_block_by_level(
        &mut self,
        block: Block,
        is_heading: bool,
        is_void: bool,
    ) -> Result<usize, String> {
        let block_level = block.level();
        debug_assert!(block_level <= 10, "Block level should be reasonable");

        let initial_stack_size = self.block_stack.len();
        let mut closed_count = 0;

        // Close blocks that are at the same level or deeper than the new block
        while let Some(current_block) = self.block_stack.last() {
            if current_block.level() >= block_level {
                self.finish_block()?;
                closed_count += 1;
            } else {
                break;
            }
        }

        debug_assert!(
            self.block_stack.len() <= initial_stack_size,
            "Stack should not grow during closing"
        );

        // Headings are self-closing, so we add them directly to the container
        if is_heading {
            // Close ALL blocks on the stack before adding heading
            // According to SYNTAX.md: "After heading" text blocks are inserted
            while !self.block_stack.is_empty() {
                self.finish_block()?;
                closed_count += 1;
            }

            // Only accumulate blocks in non-streaming mode
            if !self.streaming_mode {
                let _block_index = self.container.add_block(block);
            }
        } else if is_void {
            // Void elements are self-closing - they don't go on the stack
            // Only accumulate blocks in non-streaming mode
            if !self.streaming_mode {
                let _block_index = self.container.add_block(block);
            }
        } else {
            // Start a new block
            self.start_block(block)?;
        }

        Ok(closed_count)
    }

    /// Start a new block (push onto stack and publish event)
    fn start_block(&mut self, block: Block) -> Result<(), String> {
        let depth_before = self.current_depth();

        // Push block onto stack
        self.block_stack.push(block);

        debug_assert_eq!(
            self.current_depth(),
            depth_before + 1,
            "Depth should increase by 1 after pushing block"
        );

        Ok(())
    }

    /// Finish the current block (pop from stack and publish event)
    fn finish_block(&mut self) -> Result<(), String> {
        let depth_before = self.current_depth();
        debug_assert!(depth_before > 0, "Should have blocks on stack to finish");

        if let Some(block) = self.block_stack.pop() {
            let depth_after = self.current_depth(); // Depth after popping
            debug_assert_eq!(
                depth_after,
                depth_before - 1,
                "Depth should decrease by 1 after popping block"
            );

            // Only accumulate blocks in non-streaming mode
            if !self.streaming_mode {
                let _block_index = self.container.add_block(block);
            }
        } else {
            debug_assert!(false, "finish_block called but no block on stack");
        }

        Ok(())
    }

    /// Explicitly close the current block (when encountering './')
    /// If there's nothing to close, silently succeeds (no-op).
    fn close_current_block(&mut self) -> Result<(), String> {
        if self.block_stack.is_empty() {
            // Nothing to close - silently ignore
            return Ok(());
        }

        self.finish_block()
    }

    /// Process a content line (not a meta-line)
    fn process_content_line(&mut self, line: &str) {
        debug_assert!(
            !is_meta_line(line),
            "Content line should not be a meta line"
        );

        // If we have a block on the stack, add the line to it
        if let Some(current_block) = self.block_stack.last_mut() {
            current_block.add_content_line(line.to_string(), self.current_line_index);
        } else {
            // No block on stack, add to implicit text block
            if self.current_text_block.is_none() {
                self.current_text_block = Some(Block::new_implicit_text(self.current_line_index));
            }

            if let Some(ref mut text_block) = self.current_text_block {
                text_block.add_content_line(line.to_string(), self.current_line_index);
            } else {
                debug_assert!(false, "Text block should exist after creating it");
            }
        }
    }

    /// Finish the current implicit text block if it exists
    fn finish_current_text_block(&mut self) {
        if let Some(text_block) = self.current_text_block.take() {
            if !text_block.is_empty() && !self.streaming_mode {
                let _depth = self.current_depth();
                let _block_index = self.container.add_block(text_block);
            }
        }
    }

    /// Finish parsing (close all open blocks)
    /// Returns the number of blocks that were closed
    pub fn finish(&mut self) -> Result<usize, String> {
        // Finish any current text block
        self.finish_current_text_block();

        // Close all remaining blocks on the stack
        let mut closed_count = 0;
        while !self.block_stack.is_empty() {
            self.finish_block()?;
            closed_count += 1;
        }

        Ok(closed_count)
    }

    /// Get the completed block container
    pub fn into_container(mut self) -> Result<BlockContainer, String> {
        let _closed_count = self.finish()?;
        Ok(self.container)
    }

    /// Get a reference to the block container (blocks added so far)
    #[must_use]
    pub fn container(&self) -> &BlockContainer {
        &self.container
    }

    /// Get the current block stack (for debugging/inspection)
    #[must_use]
    pub fn block_stack(&self) -> &[Block] {
        &self.block_stack
    }

    /// Check if there are any open blocks
    #[must_use]
    pub fn has_open_blocks(&self) -> bool {
        !self.block_stack.is_empty() || self.current_text_block.is_some()
    }
}

impl Default for ElementParser {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_parser_creation() {
        let parser = ElementParser::new();
        assert_eq!(parser.current_depth(), 0);
        assert!(!parser.has_open_blocks());
    }

    #[test]
    fn test_simple_block_parsing() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line(".p", 0).unwrap();
        let _closed_count = parser.process_line("Content line 1", 0).unwrap();
        let _closed_count = parser.process_line("Content line 2", 0).unwrap();
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 1);

        let block = container.get_block(0).unwrap();
        assert_eq!(block.block_type(), "p");
    }

    #[test]
    fn test_nested_blocks() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("..p", 0).unwrap();
        let _closed_count = parser.process_line("Nested content", 0).unwrap();
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 2);

        let p_block = container.get_block(0).unwrap();
        assert_eq!(p_block.block_type(), "p");

        let div_block = container.get_block(1).unwrap();
        assert_eq!(div_block.block_type(), "div");
    }

    #[test]
    fn test_heading_block() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line(".. Heading Text", 0).unwrap();
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 1);

        let block = container.get_block(0).unwrap();
        assert_eq!(block.block_type(), "##");
        assert!(block.block_type().starts_with('#'));
    }

    #[test]
    fn test_explicit_block_closing() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("Content", 0).unwrap();
        let _closed_count = parser.process_line("./", 0).unwrap(); // Explicit close

        assert!(!parser.has_open_blocks());

        let container = parser.container();
        assert_eq!(container.len(), 1);
    }

    #[test]
    fn test_implicit_text_block() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line("Some text content", 0).unwrap();
        let _closed_count = parser.process_line("More text", 0).unwrap();
        let _closed_count = parser.process_line(".p", 0).unwrap(); // This should finish the text block
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 2); // Text block + p block

        let text_block = container.get_block(0).unwrap();
        assert_eq!(text_block.block_type(), "text");

        let p_block = container.get_block(1).unwrap();
        assert_eq!(p_block.block_type(), "p");
    }

    #[test]
    fn test_close_block_ignored_when_empty() {
        // Closing when nothing is open should be silently ignored (no-op)
        let mut parser = ElementParser::new();
        let result = parser.process_line("./", 0);
        assert!(result.is_ok());
    }

    #[test]
    fn test_invalid_meta_line_error() {
        let mut parser = ElementParser::new();
        // Actually .. is a valid heading, so let's test an invalid case
        // The parser is very permissive, so let's test empty blocks
        let result = parser.process_line(".empty", 0);
        assert!(result.is_ok());

        // Test a line that should be treated as meta but has invalid characters
        let result = parser.process_line(".", 0);
        assert!(result.is_ok()); // Even this parses as an empty type
    }

    #[test]
    fn test_line_index_monotonicity() {
        let mut parser = ElementParser::new();
        let _closed_count = parser.process_line(".p", 0).unwrap();
        let _closed_count = parser.process_line("content", 1).unwrap();

        // In debug mode, this should trigger an assertion if we go backwards
        // But since we can't test debug assertions directly in tests,
        // we'll just verify the normal flow works correctly
        let _closed_count = parser.process_line("more content", 2).unwrap();
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 1);
    }

    #[test]
    fn test_deeply_nested_blocks() {
        let mut parser = ElementParser::new();

        // Create deeply nested structure
        let _closed_count = parser.process_line(".level1", 0).unwrap();
        let _closed_count = parser.process_line("..level2", 1).unwrap();
        let _closed_count = parser.process_line("...level3", 2).unwrap();
        let _closed_count = parser.process_line("....level4", 3).unwrap();
        let _closed_count = parser.process_line(".....level5", 4).unwrap();
        let _closed_count = parser.process_line("Content at level 5", 5).unwrap();

        // Now add a block at level 2, which should close levels 3, 4, and 5
        let _closed_count = parser.process_line("..another_level2", 6).unwrap();

        parser.finish().unwrap();

        let container = parser.container();
        // We should have 6 blocks total (5 nested + 1 sibling)
        assert_eq!(container.len(), 6);
    }

    #[test]
    fn test_multiple_text_elements_in_block() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("First text", 1).unwrap();
        let _closed_count = parser.process_line("Second text", 2).unwrap();
        let _closed_count = parser.process_line("", 3).unwrap(); // Empty line
        let _closed_count = parser.process_line("Third text", 4).unwrap();
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 1);

        let div_block = container.get_block(0).unwrap();
        assert_eq!(div_block.block_type(), "div");
        assert!(!div_block.children.is_empty());
    }

    #[test]
    fn test_heading_closes_all_blocks() {
        let mut parser = ElementParser::new();

        // Create nested blocks
        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("..p", 1).unwrap();
        let _closed_count = parser.process_line("...span", 2).unwrap();
        let _closed_count = parser.process_line("Content", 3).unwrap();

        // Heading should close all blocks
        let _closed_count = parser.process_line(".. Main Heading", 4).unwrap();
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 4); // div, p, span, heading

        let heading_block = container.get_block(3).unwrap();
        assert_eq!(heading_block.block_type(), "##");
    }

    #[test]
    fn test_empty_block() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line(".empty", 0).unwrap();
        let _closed_count = parser.process_line(".next", 1).unwrap();
        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 2);

        // First block should be empty
        let empty_block = container.get_block(0).unwrap();
        assert_eq!(empty_block.block_type(), "empty");
        assert!(empty_block.children.is_empty());
    }

    #[test]
    fn test_new_creates_empty_parser() {
        let parser = ElementParser::new();

        // Test all the initialization values
        assert_eq!(parser.block_stack.len(), 0);
        assert_eq!(parser.container.len(), 0);
        assert!(parser.current_text_block.is_none());
        assert_eq!(parser.current_line_index, 0);
        assert!(parser.next_auto_id.is_none());
    }

    #[test]
    fn test_new_with_auto_id() {
        let auto_id = Arc::new(Mutex::new(42));
        let parser = ElementParser::new_with_auto_id(auto_id.clone());

        // Test that auto_id is set
        assert!(parser.next_auto_id.is_some());
        assert_eq!(parser.block_stack.len(), 0);
        assert_eq!(parser.container.len(), 0);
        assert!(parser.current_text_block.is_none());
        assert_eq!(parser.current_line_index, 0);
    }

    #[test]
    fn test_current_depth() {
        let mut parser = ElementParser::new();
        assert_eq!(parser.current_depth(), 0);

        // Add a block to increase depth
        let _closed_count = parser.process_line(".div", 0).unwrap();
        assert_eq!(parser.current_depth(), 1);

        // Add nested block
        let _closed_count = parser.process_line("..p", 1).unwrap();
        assert_eq!(parser.current_depth(), 2);

        parser.finish().unwrap();
        assert_eq!(parser.current_depth(), 0);
    }

    #[test]
    fn test_process_line_updates_line_index() {
        let mut parser = ElementParser::new();

        // Process lines with increasing indices
        let _closed_count = parser.process_line("Text line", 0).unwrap();
        assert_eq!(parser.current_line_index, 0);

        let _closed_count = parser.process_line("Another line", 5).unwrap();
        assert_eq!(parser.current_line_index, 5);

        let _closed_count = parser.process_line(".p", 10).unwrap();
        assert_eq!(parser.current_line_index, 10);
    }

    #[test]
    fn test_process_line_meta_line() {
        let mut parser = ElementParser::new();

        // Test meta line processing
        let closed_count = parser.process_line(".div", 0).unwrap();
        assert_eq!(closed_count, 0); // No blocks closed when starting new one
        assert_eq!(parser.current_depth(), 1);

        // Add content
        let closed_count = parser.process_line("Content", 1).unwrap();
        assert_eq!(closed_count, 0); // Content doesn't close blocks
    }

    #[test]
    fn test_process_line_content_line() {
        let mut parser = ElementParser::new();

        // Process content line (should create implicit text block)
        let closed_count = parser.process_line("Text content", 0).unwrap();
        assert_eq!(closed_count, 0);
        assert!(parser.current_text_block.is_some());

        // Add another content line
        let closed_count = parser.process_line("More text", 1).unwrap();
        assert_eq!(closed_count, 0);
        assert!(parser.current_text_block.is_some());
    }

    #[test]
    fn test_process_meta_line_with_explicit_close() {
        let mut parser = ElementParser::new();

        // Start a block
        let _closed_count = parser.process_line(".div", 0).unwrap();
        assert_eq!(parser.current_depth(), 1);

        // Close it explicitly
        let closed_count = parser.process_line("./", 1).unwrap();
        assert_eq!(closed_count, 1);
        assert_eq!(parser.current_depth(), 0);
    }

    #[test]
    fn test_process_meta_line_auto_id_generation() {
        let auto_id = Arc::new(Mutex::new(100));
        let mut parser = ElementParser::new_with_auto_id(auto_id.clone());

        // Process a block without explicit ID
        let _closed_count = parser.process_line(".div", 0).unwrap();

        // Check that auto ID was generated
        {
            let current_id = auto_id.lock().unwrap();
            assert_eq!(*current_id, 101); // Should have incremented
        }

        parser.finish().unwrap();
        let container = parser.container();
        let block = container.get_block(0).unwrap();
        assert!(!block.id().as_str().is_empty()); // Should have an ID
    }

    #[test]
    fn test_process_meta_line_no_auto_id_for_close_blocks() {
        let auto_id = Arc::new(Mutex::new(100));
        let mut parser = ElementParser::new_with_auto_id(auto_id.clone());

        // Start a block
        let _closed_count = parser.process_line(".div", 0).unwrap();
        let current_id_after_div = {
            let id = auto_id.lock().unwrap();
            *id
        };

        // Close block - should not generate ID
        let _closed_count = parser.process_line("./", 1).unwrap();
        let current_id_after_close = {
            let id = auto_id.lock().unwrap();
            *id
        };

        // ID should not have changed for close block
        assert_eq!(current_id_after_div, current_id_after_close);
    }

    #[test]
    fn test_process_meta_line_heading() {
        let mut parser = ElementParser::new();

        // Process a heading (empty type_name)
        let closed_count = parser.process_line(".. Heading Text", 0).unwrap();
        assert_eq!(closed_count, 0); // Headings don't close blocks by return value, but they do close all blocks
        assert_eq!(parser.current_depth(), 0); // Headings are self-closing

        let container = parser.container();
        assert_eq!(container.len(), 1);

        let heading = container.get_block(0).unwrap();
        assert_eq!(heading.block_type(), "##");
        assert!(heading.is_heading());
    }

    #[test]
    fn test_handle_block_by_level_same_level_closes_previous() {
        let mut parser = ElementParser::new();

        // Start first block at level 1
        let _closed_count = parser.process_line(".div", 0).unwrap();
        assert_eq!(parser.current_depth(), 1);

        // Start second block at same level - should close first
        let closed_count = parser.process_line(".p", 1).unwrap();
        assert_eq!(closed_count, 1); // First block should be closed
        assert_eq!(parser.current_depth(), 1); // New block on stack

        parser.finish().unwrap();
        let container = parser.container();
        assert_eq!(container.len(), 2); // Both blocks in container
    }

    #[test]
    fn test_handle_block_by_level_deeper_level_closes_shallower() {
        let mut parser = ElementParser::new();

        // Create nested structure
        let _closed_count = parser.process_line(".div", 0).unwrap(); // level 1
        let _closed_count = parser.process_line("..p", 1).unwrap(); // level 2
        let _closed_count = parser.process_line("...span", 2).unwrap(); // level 3
        assert_eq!(parser.current_depth(), 3);

        // Add block at level 2 - should close level 3 block and level 2 block
        let closed_count = parser.process_line("..another_p", 3).unwrap();
        assert_eq!(closed_count, 2); // span and p should be closed (level >= 2)
        assert_eq!(parser.current_depth(), 2); // div and new another_p on stack
    }

    #[test]
    fn test_handle_block_by_level_heading_closes_all() {
        let mut parser = ElementParser::new();

        // Create nested structure
        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("..p", 1).unwrap();
        let _closed_count = parser.process_line("...span", 2).unwrap();
        assert_eq!(parser.current_depth(), 3);

        // Add heading - should close ALL blocks
        let closed_count = parser.process_line(".. Heading", 3).unwrap();
        assert_eq!(closed_count, 3); // All blocks should be closed
        assert_eq!(parser.current_depth(), 0); // Stack should be empty

        let container = parser.container();
        assert_eq!(container.len(), 4); // span, p, div, heading
    }

    #[test]
    fn test_start_block() {
        let mut parser = ElementParser::new();
        assert_eq!(parser.current_depth(), 0);

        // Start a block
        let _closed_count = parser.process_line(".div", 0).unwrap();
        assert_eq!(parser.current_depth(), 1);

        // Start another block
        let _closed_count = parser.process_line("..p", 1).unwrap();
        assert_eq!(parser.current_depth(), 2);
    }

    #[test]
    fn test_finish_block() {
        let mut parser = ElementParser::new();

        // Start and immediately close a block
        let _closed_count = parser.process_line(".div", 0).unwrap();
        assert_eq!(parser.current_depth(), 1);

        let closed_count = parser.process_line("./", 1).unwrap();
        assert_eq!(closed_count, 1);
        assert_eq!(parser.current_depth(), 0);

        let container = parser.container();
        assert_eq!(container.len(), 1);
    }

    #[test]
    fn test_close_current_block_ignored_on_empty_stack() {
        let mut parser = ElementParser::new();

        // Closing when no blocks are open should be silently ignored
        let result = parser.process_line("./", 0);
        assert!(result.is_ok());
    }

    #[test]
    fn test_process_content_line_with_block_on_stack() {
        let mut parser = ElementParser::new();

        // Start a block
        let _closed_count = parser.process_line(".div", 0).unwrap();

        // Add content - should go to the block on stack
        let _closed_count = parser.process_line("Content line", 1).unwrap();

        parser.finish().unwrap();
        let container = parser.container();
        assert_eq!(container.len(), 1);

        let block = container.get_block(0).unwrap();
        assert!(!block.children.is_empty());
    }

    #[test]
    fn test_process_content_line_creates_implicit_text_block() {
        let mut parser = ElementParser::new();

        // Add content with no block on stack
        let _closed_count = parser.process_line("Content line", 0).unwrap();
        assert!(parser.current_text_block.is_some());

        // Add more content
        let _closed_count = parser.process_line("More content", 1).unwrap();
        assert!(parser.current_text_block.is_some());

        parser.finish().unwrap();
        let container = parser.container();
        assert_eq!(container.len(), 1);

        let text_block = container.get_block(0).unwrap();
        assert_eq!(text_block.block_type(), "text");
        assert!(text_block.is_implicit_text());
    }

    #[test]
    fn test_finish_current_text_block_empty() {
        let mut parser = ElementParser::new();

        // Ensure no text block exists
        assert!(parser.current_text_block.is_none());

        // Process a meta line (which calls finish_current_text_block internally)
        let _closed_count = parser.process_line(".div", 0).unwrap();

        // Should handle empty text block gracefully
        assert!(parser.current_text_block.is_none());
    }

    #[test]
    fn test_finish_current_text_block_with_content() {
        let mut parser = ElementParser::new();

        // Create implicit text block
        let _closed_count = parser.process_line("Text content", 0).unwrap();
        assert!(parser.current_text_block.is_some());

        // Process meta line should finish text block
        let _closed_count = parser.process_line(".div", 1).unwrap();
        assert!(parser.current_text_block.is_none());

        let container = parser.container();
        assert_eq!(container.len(), 1); // Text block should be in container

        let text_block = container.get_block(0).unwrap();
        assert_eq!(text_block.block_type(), "text");
    }

    #[test]
    fn test_finish_closes_all_blocks() {
        let mut parser = ElementParser::new();

        // Create nested structure
        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("..p", 1).unwrap();
        let _closed_count = parser.process_line("...span", 2).unwrap();
        assert_eq!(parser.current_depth(), 3);

        // Add some content
        let _closed_count = parser.process_line("Content", 3).unwrap();

        // Finish should close all blocks
        let closed_count = parser.finish().unwrap();
        assert_eq!(closed_count, 3);
        assert_eq!(parser.current_depth(), 0);

        let container = parser.container();
        assert_eq!(container.len(), 3); // All three blocks
    }

    #[test]
    fn test_finish_with_text_block() {
        let mut parser = ElementParser::new();

        // Create text content
        let _closed_count = parser.process_line("Text content", 0).unwrap();
        assert!(parser.current_text_block.is_some());

        // Create regular block
        let _closed_count = parser.process_line(".div", 1).unwrap();
        assert_eq!(parser.current_depth(), 1);
        assert!(parser.current_text_block.is_none()); // Should be finished

        // Finish everything
        let closed_count = parser.finish().unwrap();
        assert_eq!(closed_count, 1); // Only div block closed

        let container = parser.container();
        assert_eq!(container.len(), 2); // Text block + div block
    }

    #[test]
    fn test_into_container() {
        let mut parser = ElementParser::new();

        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("Content", 1).unwrap();

        let container = parser.into_container().unwrap();
        assert_eq!(container.len(), 1);

        let block = container.get_block(0).unwrap();
        assert_eq!(block.block_type(), "div");
    }

    #[test]
    fn test_container_reference() {
        let mut parser = ElementParser::new();

        // Initially empty
        assert_eq!(parser.container().len(), 0);

        // Add and close a block
        let _closed_count = parser.process_line(".div", 0).unwrap();
        let _closed_count = parser.process_line("./", 1).unwrap();

        // Should now have one block
        assert_eq!(parser.container().len(), 1);
    }

    #[test]
    fn test_block_stack_reference() {
        let mut parser = ElementParser::new();

        // Initially empty
        assert_eq!(parser.block_stack().len(), 0);

        // Add a block
        let _closed_count = parser.process_line(".div", 0).unwrap();
        assert_eq!(parser.block_stack().len(), 1);

        // Add nested block
        let _closed_count = parser.process_line("..p", 1).unwrap();
        assert_eq!(parser.block_stack().len(), 2);

        // Close one
        let _closed_count = parser.process_line("./", 2).unwrap();
        assert_eq!(parser.block_stack().len(), 1);
    }

    #[test]
    fn test_has_open_blocks_with_stack() {
        let mut parser = ElementParser::new();

        assert!(!parser.has_open_blocks());

        // Add block to stack
        let _closed_count = parser.process_line(".div", 0).unwrap();
        assert!(parser.has_open_blocks());

        // Close block
        let _closed_count = parser.process_line("./", 1).unwrap();
        assert!(!parser.has_open_blocks());
    }

    #[test]
    fn test_has_open_blocks_with_text_block() {
        let mut parser = ElementParser::new();

        assert!(!parser.has_open_blocks());

        // Add content (creates text block)
        let _closed_count = parser.process_line("Text content", 0).unwrap();
        assert!(parser.has_open_blocks());

        // Process meta line (finishes text block)
        let _closed_count = parser.process_line(".div", 1).unwrap();
        assert!(parser.has_open_blocks()); // div block is now on stack

        // Close div block
        let _closed_count = parser.process_line("./", 2).unwrap();
        assert!(!parser.has_open_blocks());
    }

    #[test]
    fn test_default_implementation() {
        let parser: ElementParser = Default::default();
        assert_eq!(parser.current_depth(), 0);
        assert!(!parser.has_open_blocks());
        assert_eq!(parser.container().len(), 0);
    }

    #[test]
    fn test_auto_id_with_text_block() {
        let auto_id = Arc::new(Mutex::new(50));
        let mut parser = ElementParser::new_with_auto_id(auto_id.clone());

        // Text blocks should not get auto IDs
        let _closed_count = parser.process_line("Text content", 0).unwrap();
        let _closed_count = parser.process_line(".div", 1).unwrap(); // This should finish text block

        // Only div should have gotten an auto ID
        let current_id = {
            let id = auto_id.lock().unwrap();
            *id
        };
        assert_eq!(current_id, 51); // Should have incremented only once
    }

    #[test]
    fn test_complex_nesting_and_closing_pattern() {
        let mut parser = ElementParser::new();

        // Create complex nested structure
        let _closed_count = parser.process_line(".outer", 0).unwrap();
        let _closed_count = parser.process_line("..middle1", 1).unwrap();
        let _closed_count = parser.process_line("...inner", 2).unwrap();
        let _closed_count = parser.process_line("Inner content", 3).unwrap();

        // Jump back to level 2 - should close inner and middle1 (both >= level 2)
        let closed_count = parser.process_line("..middle2", 4).unwrap();
        assert_eq!(closed_count, 2); // inner and middle1 closed

        // Add content to middle2
        let _closed_count = parser.process_line("Middle2 content", 5).unwrap();

        // Jump to level 1 - should close middle2 and outer (both >= level 1)
        let closed_count = parser.process_line(".sibling", 6).unwrap();
        assert_eq!(closed_count, 2); // middle2 and outer closed

        parser.finish().unwrap();

        let container = parser.container();
        assert_eq!(container.len(), 5); // inner, middle1, middle2, outer, sibling
    }

    #[test]
    fn test_meta_line_parsing_failure_handling() {
        let mut parser = ElementParser::new();

        // The MetaLine::parse is very permissive, so we test a scenario
        // that should work but exercise the error path conceptually
        let result = parser.process_line(".valid_block", 0);
        assert!(result.is_ok());

        // Even edge cases like just a dot should work
        let result = parser.process_line(".", 1);
        assert!(result.is_ok());

        parser.finish().unwrap();
    }
}
