use crate::events::{Consumer, Event};
use crate::parsers::wasot::parser::TargetType;
use crate::parsers::wasot::{Block, Footnote, Link, MetaLine, QuotedString};
use std::io::{BufWriter, Write};
use std::sync::{Arc, Mutex};

/// Validate that a string is valid S-expression/Scheme syntax using tree-sitter-scheme.
///
/// Returns `Ok(())` if the syntax is valid, or `Err` with an error message if invalid.
///
/// # Example
/// ```text
/// use wasot_compiler::consumers::to_sexp_consumer::validate_sexp;
///
/// assert!(validate_sexp("(block type: \"test\")").is_ok());
/// assert!(validate_sexp("(unclosed").is_err());
/// ```
pub fn validate_sexp(sexp: &str) -> Result<(), String> {
    let mut parser = tree_sitter::Parser::new();
    let language = tree_sitter_scheme::LANGUAGE;
    parser
        .set_language(&language.into())
        .map_err(|e| format!("Failed to load Scheme parser: {e}"))?;

    let tree = parser
        .parse(sexp, None)
        .ok_or_else(|| "Failed to parse S-expression".to_string())?;

    if tree.root_node().has_error() {
        // Find the first error node for a more helpful message
        let root = tree.root_node();
        let mut cursor = root.walk();
        let mut error_info = String::new();

        fn find_error(cursor: &mut tree_sitter::TreeCursor, info: &mut String) -> bool {
            let node = cursor.node();
            if node.is_error() || node.is_missing() {
                let start = node.start_position();
                *info = format!(
                    "Syntax error at line {}, column {}: {}",
                    start.row + 1,
                    start.column + 1,
                    if node.is_missing() {
                        "missing node"
                    } else {
                        "invalid syntax"
                    }
                );
                return true;
            }
            if cursor.goto_first_child() {
                loop {
                    if find_error(cursor, info) {
                        return true;
                    }
                    if !cursor.goto_next_sibling() {
                        break;
                    }
                }
                cursor.goto_parent();
            }
            false
        }

        find_error(&mut cursor, &mut error_info);
        Err(if error_info.is_empty() {
            "S-expression contains syntax errors".to_string()
        } else {
            error_info
        })
    } else {
        Ok(())
    }
}

/// S-expression converter that listens to block events and generates S-expression output
/// Uses tree-sitter style: (node-type key: value key: value ...children...)
///
/// This format captures all wasot data model details including:
/// - Quoted flags on strings
/// - Link arguments and target types
/// - Embedded links in QuotedStrings
pub struct ToSexpConsumer<W: Write> {
    writer: BufWriter<W>,
    indent_level: usize,
    pretty_print: bool,
    block_stack: Vec<Block>,
}

impl<W: Write> ToSexpConsumer<W> {
    /// Create a new S-expression converter with the given writer
    pub fn new(writer: W, pretty_print: bool) -> Self {
        ToSexpConsumer {
            writer: BufWriter::new(writer),
            indent_level: 0,
            pretty_print,
            block_stack: Vec::new(),
        }
    }

    /// Write indentation based on current level
    fn write_indent(&mut self) -> std::io::Result<()> {
        if self.pretty_print {
            for _ in 0..self.indent_level {
                write!(self.writer, "  ")?;
            }
        }
        Ok(())
    }

    /// Write a newline if pretty printing
    fn write_newline(&mut self) -> std::io::Result<()> {
        if self.pretty_print {
            writeln!(self.writer)?;
        }
        Ok(())
    }

    /// Escape a string for S-expression output
    fn escape_string(s: &str) -> String {
        s.replace('\\', "\\\\")
            .replace('"', "\\\"")
            .replace('\n', "\\n")
            .replace('\r', "\\r")
            .replace('\t', "\\t")
    }

    /// Write a QuotedString with its quoted flag and any embedded links
    fn write_quoted_string(&mut self, qs: &QuotedString, node_name: &str) -> std::io::Result<()> {
        self.write_indent()?;
        write!(
            self.writer,
            "({} content: \"{}\" quoted: {}",
            node_name,
            Self::escape_string(&qs.content),
            qs.quoted
        )?;

        // Write embedded links if any
        if !qs.links.is_empty() {
            self.write_newline()?;
            self.indent_level += 1;
            for link in &qs.links {
                self.write_link_full(link)?;
            }
            self.indent_level = self.indent_level.saturating_sub(1);
            self.write_indent()?;
        }

        write!(self.writer, ")")?;
        self.write_newline()?;
        Ok(())
    }

    /// Write arguments (positional and keyword)
    fn write_arguments(
        &mut self,
        args: &[QuotedString],
        kwargs: &[(QuotedString, QuotedString)],
    ) -> std::io::Result<()> {
        // Write positional arguments
        for arg in args {
            self.write_quoted_string(arg, "arg")?;
        }

        // Write keyword arguments
        for (key, value) in kwargs {
            self.write_indent()?;
            write!(self.writer, "(kwarg")?;
            self.write_newline()?;
            self.indent_level += 1;

            // Key as QuotedString
            self.write_indent()?;
            write!(
                self.writer,
                "(key content: \"{}\" quoted: {})",
                Self::escape_string(&key.content),
                key.quoted
            )?;
            self.write_newline()?;

            // Value as QuotedString with potential embedded links
            self.write_indent()?;
            write!(
                self.writer,
                "(value content: \"{}\" quoted: {}",
                Self::escape_string(&value.content),
                value.quoted
            )?;
            if !value.links.is_empty() {
                self.write_newline()?;
                self.indent_level += 1;
                for link in &value.links {
                    self.write_link_full(link)?;
                }
                self.indent_level = self.indent_level.saturating_sub(1);
                self.write_indent()?;
            }
            write!(self.writer, ")")?;
            self.write_newline()?;

            self.indent_level = self.indent_level.saturating_sub(1);
            self.write_indent()?;
            write!(self.writer, ")")?;
            self.write_newline()?;
        }

        Ok(())
    }

    /// Write a block start with its attributes
    fn write_block_start(&mut self, block: &Block) -> std::io::Result<()> {
        self.write_indent()?;
        write!(
            self.writer,
            "(block type: \"{}\" level: {}",
            Self::escape_string(block.block_type()),
            block.level()
        )?;

        // Optional id attribute
        if !block.id().value.is_empty() {
            write!(
                self.writer,
                " id: \"{}\"",
                Self::escape_string(&block.id().value)
            )?;
        }

        // Optional line_index
        if let Some(line_idx) = block.meta_line.line_index {
            write!(self.writer, " line_index: {line_idx}")?;
        }

        // Check for void flag
        if block.meta_line.is_void {
            write!(self.writer, " void: true")?;
        }

        self.write_newline()?;
        self.indent_level += 1;

        // Write inline text as QuotedString if present
        if !block.inline_text().content.is_empty() || block.inline_text().quoted {
            self.write_quoted_string(block.inline_text(), "inline-text")?;
        }

        // Write arguments
        self.write_arguments(&block.arguments().args, &block.arguments().kwargs)?;

        Ok(())
    }

    /// Write a block end
    fn write_block_end(&mut self) -> std::io::Result<()> {
        self.indent_level = self.indent_level.saturating_sub(1);
        self.write_indent()?;
        write!(self.writer, ")")?;
        self.write_newline()?;
        Ok(())
    }

    /// Write a void element
    fn write_void_element(&mut self, meta_line: &MetaLine) -> std::io::Result<()> {
        self.write_indent()?;
        write!(
            self.writer,
            "(void type: \"{}\" level: {}",
            Self::escape_string(&meta_line.type_name),
            meta_line.level
        )?;

        // Optional id
        if !meta_line.id.value.is_empty() {
            write!(
                self.writer,
                " id: \"{}\"",
                Self::escape_string(&meta_line.id.value)
            )?;
        }

        // Optional line_index
        if let Some(line_idx) = meta_line.line_index {
            write!(self.writer, " line_index: {line_idx}")?;
        }

        // Check if we have content to write
        let has_content = !meta_line.inline_text.content.is_empty()
            || meta_line.inline_text.quoted
            || !meta_line.arguments.args.is_empty()
            || !meta_line.arguments.kwargs.is_empty();

        if has_content {
            self.write_newline()?;
            self.indent_level += 1;

            // Inline text as QuotedString
            if !meta_line.inline_text.content.is_empty() || meta_line.inline_text.quoted {
                self.write_quoted_string(&meta_line.inline_text, "inline-text")?;
            }

            // Arguments
            self.write_arguments(&meta_line.arguments.args, &meta_line.arguments.kwargs)?;

            self.indent_level = self.indent_level.saturating_sub(1);
            self.write_indent()?;
        }

        write!(self.writer, ")")?;
        self.write_newline()?;
        Ok(())
    }

    /// Write a context line
    fn write_context_line(&mut self, meta_line: &MetaLine) -> std::io::Result<()> {
        self.write_indent()?;
        write!(
            self.writer,
            "(context-line type: \"{}\" level: {}",
            Self::escape_string(&meta_line.type_name),
            meta_line.level
        )?;

        // Optional id
        if !meta_line.id.value.is_empty() {
            write!(
                self.writer,
                " id: \"{}\"",
                Self::escape_string(&meta_line.id.value)
            )?;
        }

        // Optional line_index
        if let Some(line_idx) = meta_line.line_index {
            write!(self.writer, " line_index: {line_idx}")?;
        }

        // Check if we have content to write
        let has_content = !meta_line.inline_text.content.is_empty()
            || meta_line.inline_text.quoted
            || !meta_line.arguments.args.is_empty()
            || !meta_line.arguments.kwargs.is_empty();

        if has_content {
            self.write_newline()?;
            self.indent_level += 1;

            // Inline text as QuotedString
            if !meta_line.inline_text.content.is_empty() || meta_line.inline_text.quoted {
                self.write_quoted_string(&meta_line.inline_text, "inline-text")?;
            }

            // Arguments
            self.write_arguments(&meta_line.arguments.args, &meta_line.arguments.kwargs)?;

            self.indent_level = self.indent_level.saturating_sub(1);
            self.write_indent()?;
        }

        write!(self.writer, ")")?;
        self.write_newline()?;
        Ok(())
    }

    /// Write text content with links and footnotes
    fn write_text(
        &mut self,
        lines: &[String],
        links: &[Vec<Link>],
        footnotes: &[Footnote],
    ) -> std::io::Result<()> {
        self.write_indent()?;
        write!(self.writer, "(text")?;

        let has_content =
            !lines.is_empty() || links.iter().any(|l| !l.is_empty()) || !footnotes.is_empty();

        if has_content {
            self.write_newline()?;
            self.indent_level += 1;

            // Write each text line
            for (line_idx, line) in lines.iter().enumerate() {
                self.write_indent()?;
                write!(
                    self.writer,
                    "(line index: {line_idx} content: \"{}\")",
                    Self::escape_string(line)
                )?;
                self.write_newline()?;

                // Write links for this line
                if let Some(line_links) = links.get(line_idx) {
                    for link in line_links {
                        self.write_link_full(link)?;
                    }
                }
            }

            // Write footnotes
            for footnote in footnotes {
                self.write_footnote(footnote)?;
            }

            self.indent_level = self.indent_level.saturating_sub(1);
            self.write_indent()?;
        }

        write!(self.writer, ")")?;
        self.write_newline()?;
        Ok(())
    }

    /// Write a full link with all details including target_type and arguments
    fn write_link_full(&mut self, link: &Link) -> std::io::Result<()> {
        self.write_indent()?;
        write!(self.writer, "(link")?;

        // Target type
        let target_type_str = match &link.target_type {
            TargetType::Uri => "uri",
            TargetType::Id => "id",
            TargetType::FootNote => "footnote",
            TargetType::IdInDocument(ref ext_ref) => {
                // For cross-document references, we'll output the type and details
                write!(self.writer, " target_type: id_in_document")?;
                write!(
                    self.writer,
                    " doc_id: \"{}\" local_id: \"{}\"",
                    Self::escape_string(&ext_ref.document_id.value),
                    Self::escape_string(&ext_ref.local_id.value)
                )?;
                if let Some(ref path) = ext_ref.document_path {
                    write!(self.writer, " doc_path: \"{}\"", Self::escape_string(path))?;
                }
                "" // Don't write target_type again
            }
        };

        if !target_type_str.is_empty() {
            write!(self.writer, " target_type: {target_type_str}")?;
        }

        // Position info
        write!(
            self.writer,
            " start: {} length: {}",
            link.start_index, link.length
        )?;

        // Target
        if let Some(ref target) = link.target {
            write!(self.writer, " target: \"{}\"", Self::escape_string(target))?;
        }

        // Check if we have name or arguments to write as children
        let has_children = link.name.is_some()
            || !link.arguments.args.is_empty()
            || !link.arguments.kwargs.is_empty();

        if has_children {
            self.write_newline()?;
            self.indent_level += 1;

            // Name as QuotedString
            if let Some(ref name) = link.name {
                self.write_quoted_string(name, "name")?;
            }

            // Link arguments
            if !link.arguments.args.is_empty() || !link.arguments.kwargs.is_empty() {
                self.write_indent()?;
                write!(self.writer, "(arguments")?;
                self.write_newline()?;
                self.indent_level += 1;
                self.write_arguments(&link.arguments.args, &link.arguments.kwargs)?;
                self.indent_level = self.indent_level.saturating_sub(1);
                self.write_indent()?;
                write!(self.writer, ")")?;
                self.write_newline()?;
            }

            self.indent_level = self.indent_level.saturating_sub(1);
            self.write_indent()?;
        }

        write!(self.writer, ")")?;
        self.write_newline()?;
        Ok(())
    }

    /// Write a single footnote
    fn write_footnote(&mut self, footnote: &Footnote) -> std::io::Result<()> {
        self.write_indent()?;
        write!(
            self.writer,
            "(footnote marker: {} link: \"{}\")",
            footnote.marker,
            Self::escape_string(&footnote.link)
        )?;
        self.write_newline()?;
        Ok(())
    }

    /// Finish writing and return the underlying writer
    pub fn finish(mut self) -> std::io::Result<W> {
        self.writer.flush()?;
        Ok(self.writer.into_inner()?)
    }

    /// Process an event
    fn process_event(&mut self, event: &Event) {
        match event {
            Event::BlockStarted { meta_line, .. } => {
                // Check if this is a void block - handle immediately
                if meta_line.is_void {
                    if let Err(e) = self.write_void_element(meta_line) {
                        eprintln!("S-expression conversion error on void block: {e}");
                    }
                    return;
                }

                // Regular block - create and write start
                let block = Block::new(meta_line.clone());
                if let Err(e) = self.write_block_start(&block) {
                    eprintln!("S-expression conversion error on block start: {e}");
                }
                self.block_stack.push(block);
            }
            Event::BlockFinished => {
                if self.block_stack.pop().is_some() {
                    if let Err(e) = self.write_block_end() {
                        eprintln!("S-expression conversion error on block finish: {e}");
                    }
                } else {
                    eprintln!("Warning: BlockFinished event without matching BlockStarted");
                }
            }
            Event::TextFinished {
                lines,
                links,
                footnotes,
            } => {
                if !lines.is_empty() {
                    if let Err(e) = self.write_text(lines, links, footnotes) {
                        eprintln!("S-expression conversion error on text: {e}");
                    }
                }
            }
            Event::ContextLine { meta_line } => {
                if let Err(e) = self.write_context_line(meta_line) {
                    eprintln!("S-expression conversion error on context line: {e}");
                }
            }
            Event::VoidElement { meta_line } => {
                if let Err(e) = self.write_void_element(meta_line) {
                    eprintln!("S-expression conversion error on void element: {e}");
                }
            }
        }
    }
}

/// Consumer implementation for arbitrary writers
impl<W: Write + Send + Sync + 'static> Consumer for ToSexpConsumer<W> {
    fn handle(&mut self, event: Event) {
        self.process_event(&event);
    }
}

/// Consumer implementation for shared writer access
impl<W: Write + Send + Sync + 'static> Consumer for Arc<Mutex<ToSexpConsumer<W>>> {
    fn handle(&mut self, event: Event) {
        let mut converter = self.lock().unwrap();
        converter.process_event(&event);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::events::Event;
    use crate::parsers::wasot::arguments::ParsedArguments;
    use crate::parsers::wasot::Id;

    #[allow(clippy::type_complexity)]
    fn create_test_meta_line(
        block_type: &str,
        level: usize,
        inline_text: &str,
        inline_quoted: bool,
        id: &str,
        args: Vec<(&str, bool)>,
        kwargs: Vec<((&str, bool), (&str, bool))>,
    ) -> MetaLine {
        let args: Vec<QuotedString> = args
            .into_iter()
            .map(|(s, q)| QuotedString::new(s.to_string(), q))
            .collect();
        let kwargs: Vec<(QuotedString, QuotedString)> = kwargs
            .into_iter()
            .map(|((k, kq), (v, vq))| {
                (
                    QuotedString::new(k.to_string(), kq),
                    QuotedString::new(v.to_string(), vq),
                )
            })
            .collect();

        MetaLine {
            type_name: block_type.to_string(),
            level,
            inline_text: QuotedString::new(inline_text.to_string(), inline_quoted),
            id: if id.is_empty() {
                Id::empty()
            } else {
                Id::new(id.to_string())
            },
            line_index: Some(0),
            is_void: false,
            arguments: ParsedArguments { args, kwargs },
        }
    }

    fn get_sexp_output(consumer: ToSexpConsumer<Vec<u8>>) -> String {
        let bytes = consumer.finish().unwrap();
        let output = String::from_utf8(bytes).unwrap();

        // Validate S-expression syntax using tree-sitter-scheme
        if !output.is_empty() {
            if let Err(e) = validate_sexp(&output) {
                panic!("Invalid S-expression output:\n{output}\n\nError: {e}");
            }
        }

        output
    }

    #[test]
    fn test_validate_sexp_valid() {
        // Simple valid S-expressions
        assert!(validate_sexp("(block)").is_ok());
        assert!(validate_sexp("(block type: \"test\")").is_ok());
        assert!(validate_sexp("(a (b (c)))").is_ok());
        assert!(validate_sexp("(block type: \"test\" level: 1)").is_ok());
    }

    #[test]
    fn test_validate_sexp_invalid() {
        // Unclosed parenthesis
        assert!(validate_sexp("(unclosed").is_err());
        // Extra closing parenthesis
        assert!(validate_sexp("(block))").is_err());
        // Unclosed string
        assert!(validate_sexp("(block \"unclosed)").is_err());
    }

    #[test]
    fn test_validate_sexp_empty() {
        // Empty input is valid (no syntax errors)
        assert!(validate_sexp("").is_ok());
    }

    #[test]
    fn test_validate_sexp_nested() {
        let nested = r#"(block type: "outer"
          (block type: "inner"
            (text "hello")
          )
        )"#;
        assert!(validate_sexp(nested).is_ok());
    }

    #[test]
    fn test_new_consumer() {
        let buffer = Vec::new();
        let consumer = ToSexpConsumer::new(buffer, true);
        assert!(consumer.pretty_print);
        assert_eq!(consumer.indent_level, 0);
        assert!(consumer.block_stack.is_empty());
    }

    #[test]
    fn test_escape_string() {
        assert_eq!(ToSexpConsumer::<Vec<u8>>::escape_string("hello"), "hello");
        assert_eq!(
            ToSexpConsumer::<Vec<u8>>::escape_string("hello\"world"),
            "hello\\\"world"
        );
        assert_eq!(
            ToSexpConsumer::<Vec<u8>>::escape_string("hello\nworld"),
            "hello\\nworld"
        );
        assert_eq!(
            ToSexpConsumer::<Vec<u8>>::escape_string("hello\\world"),
            "hello\\\\world"
        );
    }

    #[test]
    fn test_simple_block() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("paragraph", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(block type: \"paragraph\" level: 1"));
        assert!(output.contains("line_index: 0"));
        assert!(output.contains(")"));
    }

    #[test]
    fn test_block_with_id() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("section", 2, "", false, "my-id", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("id: \"my-id\""));
    }

    #[test]
    fn test_block_with_inline_text_quoted() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        // Test with quoted inline text
        let meta_line = create_test_meta_line("heading", 1, "Main Title", true, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(inline-text content: \"Main Title\" quoted: true)"));
    }

    #[test]
    fn test_block_with_inline_text_unquoted() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        // Test with unquoted inline text
        let meta_line =
            create_test_meta_line("heading", 1, "Main Title", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(inline-text content: \"Main Title\" quoted: false)"));
    }

    #[test]
    fn test_block_with_quoted_args() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line(
            "div",
            1,
            "",
            false,
            "",
            vec![("arg1", false), ("arg with spaces", true)],
            vec![],
        );
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(arg content: \"arg1\" quoted: false)"));
        assert!(output.contains("(arg content: \"arg with spaces\" quoted: true)"));
    }

    #[test]
    fn test_block_with_kwargs_quoted_flags() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line(
            "code",
            1,
            "",
            false,
            "",
            vec![],
            vec![
                (("language", false), ("rust", false)),
                (("filename", false), ("my file.rs", true)),
            ],
        );
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(key content: \"language\" quoted: false)"));
        assert!(output.contains("(value content: \"rust\" quoted: false)"));
        assert!(output.contains("(key content: \"filename\" quoted: false)"));
        assert!(output.contains("(value content: \"my file.rs\" quoted: true)"));
    }

    #[test]
    fn test_text_with_line_indices() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::TextFinished {
            lines: vec!["Hello world".to_string(), "Second line".to_string()],
            links: vec![vec![], vec![]],
            footnotes: vec![],
        });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(line index: 0 content: \"Hello world\")"));
        assert!(output.contains("(line index: 1 content: \"Second line\")"));
    }

    #[test]
    fn test_nested_blocks() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        // Outer block
        let outer_meta = create_test_meta_line("section", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted {
            meta_line: outer_meta,
        });

        // Inner block
        let inner_meta = create_test_meta_line("paragraph", 2, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted {
            meta_line: inner_meta,
        });
        consumer.process_event(&Event::BlockFinished);

        // Close outer
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        // Both blocks should be present
        assert!(output.contains("type: \"section\""));
        assert!(output.contains("type: \"paragraph\""));
    }

    #[test]
    fn test_void_element() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let mut meta_line = create_test_meta_line("hr", 1, "", false, "", vec![], vec![]);
        meta_line.is_void = true;
        consumer.process_event(&Event::VoidElement { meta_line });

        let output = get_sexp_output(consumer);
        assert!(output.contains("(void type: \"hr\" level: 1"));
    }

    #[test]
    fn test_context_line() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("@document", 1, "doc-123", false, "", vec![], vec![]);
        consumer.process_event(&Event::ContextLine { meta_line });

        let output = get_sexp_output(consumer);
        assert!(output.contains("(context-line type: \"@document\""));
        assert!(output.contains("(inline-text content: \"doc-123\" quoted: false)"));
    }

    #[test]
    fn test_compact_output() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, false); // Not pretty printing

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        // Without pretty print, fewer newlines
        assert!(output.lines().count() <= 2);
    }

    // -------------------------------------------------------------------------
    // Link tests
    // -------------------------------------------------------------------------

    fn create_uri_link(name: &str, target: &str, start: usize, length: usize) -> Link {
        Link {
            name: Some(QuotedString::new(name.to_string(), false)),
            target: Some(target.to_string()),
            target_type: TargetType::Uri,
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            start_index: start,
            length,
        }
    }

    fn create_id_link(name: &str, target: &str, start: usize, length: usize) -> Link {
        Link {
            name: Some(QuotedString::new(name.to_string(), false)),
            target: Some(target.to_string()),
            target_type: TargetType::Id,
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            start_index: start,
            length,
        }
    }

    fn create_footnote_link(marker: &str, start: usize, length: usize) -> Link {
        Link {
            name: None,
            target: Some(marker.to_string()),
            target_type: TargetType::FootNote,
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            start_index: start,
            length,
        }
    }

    #[test]
    fn test_text_with_uri_link() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        let link = create_uri_link("click here", "https://example.com", 5, 10);
        consumer.process_event(&Event::TextFinished {
            lines: vec!["Check click here now".to_string()],
            links: vec![vec![link]],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(link"));
        assert!(output.contains("target_type: uri"));
        assert!(output.contains("target: \"https://example.com\""));
        assert!(output.contains("start: 5"));
        assert!(output.contains("length: 10"));
        assert!(output.contains("(name content: \"click here\""));
    }

    #[test]
    fn test_text_with_id_link() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        let link = create_id_link("see section", "section-1", 0, 11);
        consumer.process_event(&Event::TextFinished {
            lines: vec!["see section for more".to_string()],
            links: vec![vec![link]],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("target_type: id"));
        assert!(output.contains("target: \"section-1\""));
    }

    #[test]
    fn test_text_with_footnote_link() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        let link = create_footnote_link("*1", 10, 2);
        consumer.process_event(&Event::TextFinished {
            lines: vec!["Some text *1 here".to_string()],
            links: vec![vec![link]],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("target_type: footnote"));
        assert!(output.contains("target: \"*1\""));
    }

    #[test]
    fn test_text_with_multiple_links_on_same_line() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        let link1 = create_uri_link("first", "https://first.com", 0, 5);
        let link2 = create_uri_link("second", "https://second.com", 10, 6);
        consumer.process_event(&Event::TextFinished {
            lines: vec!["first and second links".to_string()],
            links: vec![vec![link1, link2]],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("target: \"https://first.com\""));
        assert!(output.contains("target: \"https://second.com\""));
    }

    #[test]
    fn test_text_with_links_on_multiple_lines() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        let link1 = create_uri_link("link1", "https://one.com", 5, 5);
        let link2 = create_uri_link("link2", "https://two.com", 3, 5);
        consumer.process_event(&Event::TextFinished {
            lines: vec!["Line link1 here".to_string(), "Go link2 now".to_string()],
            links: vec![vec![link1], vec![link2]],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(line index: 0"));
        assert!(output.contains("(line index: 1"));
        assert!(output.contains("https://one.com"));
        assert!(output.contains("https://two.com"));
    }

    // -------------------------------------------------------------------------
    // Footnote tests
    // -------------------------------------------------------------------------

    #[test]
    fn test_text_with_footnotes() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        consumer.process_event(&Event::TextFinished {
            lines: vec!["Text with footnote marker".to_string()],
            links: vec![vec![]],
            footnotes: vec![
                Footnote {
                    marker: 1,
                    link: "https://example.com/ref".to_string(),
                },
                Footnote {
                    marker: 2,
                    link: "https://example.com/ref2".to_string(),
                },
            ],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(footnote marker: 1 link: \"https://example.com/ref\")"));
        assert!(output.contains("(footnote marker: 2 link: \"https://example.com/ref2\")"));
    }

    // -------------------------------------------------------------------------
    // Link arguments tests
    // -------------------------------------------------------------------------

    #[test]
    fn test_link_with_arguments() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        let link = Link {
            name: Some(QuotedString::new("my link".to_string(), true)),
            target: Some("target-id".to_string()),
            target_type: TargetType::Id,
            arguments: ParsedArguments {
                args: vec![QuotedString::new("arg1".to_string(), false)],
                kwargs: vec![(
                    QuotedString::new("key".to_string(), false),
                    QuotedString::new("value".to_string(), true),
                )],
            },
            start_index: 0,
            length: 7,
        };

        consumer.process_event(&Event::TextFinished {
            lines: vec!["my link text".to_string()],
            links: vec![vec![link]],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(name content: \"my link\" quoted: true)"));
        assert!(output.contains("(arguments"));
        assert!(output.contains("(arg content: \"arg1\" quoted: false)"));
        assert!(output.contains("(kwarg"));
        assert!(output.contains("(key content: \"key\" quoted: false)"));
        assert!(output.contains("(value content: \"value\" quoted: true"));
    }

    // -------------------------------------------------------------------------
    // Special character escaping tests
    // -------------------------------------------------------------------------

    #[test]
    fn test_escape_quotes_in_content() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line =
            create_test_meta_line("p", 1, "Text with \"quotes\"", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("\\\"quotes\\\""));
    }

    #[test]
    fn test_escape_backslashes_in_content() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line =
            create_test_meta_line("p", 1, "Path: C:\\Users\\file", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("C:\\\\Users\\\\file"));
    }

    #[test]
    fn test_escape_newlines_in_inline_text() {
        // Note: newlines in inline text should be escaped
        assert_eq!(
            ToSexpConsumer::<Vec<u8>>::escape_string("line1\nline2"),
            "line1\\nline2"
        );
    }

    #[test]
    fn test_escape_tabs() {
        assert_eq!(
            ToSexpConsumer::<Vec<u8>>::escape_string("col1\tcol2"),
            "col1\\tcol2"
        );
    }

    #[test]
    fn test_escape_carriage_return() {
        assert_eq!(
            ToSexpConsumer::<Vec<u8>>::escape_string("line1\r\nline2"),
            "line1\\r\\nline2"
        );
    }

    // -------------------------------------------------------------------------
    // Edge cases
    // -------------------------------------------------------------------------

    #[test]
    fn test_empty_block() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("empty", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });
        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(block type: \"empty\""));
        // No text section
        assert!(!output.contains("(text"));
    }

    #[test]
    fn test_deeply_nested_blocks() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        // Create 5 levels of nesting
        for level in 1..=5 {
            let meta_line = create_test_meta_line(
                &format!("level{level}"),
                level,
                "",
                false,
                "",
                vec![],
                vec![],
            );
            consumer.process_event(&Event::BlockStarted { meta_line });
        }

        // Close all 5 levels
        for _ in 0..5 {
            consumer.process_event(&Event::BlockFinished);
        }

        let output = get_sexp_output(consumer);

        // Verify all levels present
        assert!(output.contains("type: \"level1\""));
        assert!(output.contains("type: \"level5\""));

        // Verify parentheses are balanced
        let open_count = output.matches('(').count();
        let close_count = output.matches(')').count();
        assert_eq!(open_count, close_count, "Parentheses should be balanced");
    }

    #[test]
    fn test_void_element_with_inline_text() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let mut meta_line = create_test_meta_line("img", 1, "alt text", true, "", vec![], vec![]);
        meta_line.is_void = true;
        consumer.process_event(&Event::VoidElement { meta_line });

        let output = get_sexp_output(consumer);
        assert!(output.contains("(void type: \"img\""));
        assert!(output.contains("(inline-text content: \"alt text\" quoted: true)"));
    }

    #[test]
    fn test_void_element_with_kwargs() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let mut meta_line = create_test_meta_line(
            "img",
            1,
            "",
            false,
            "",
            vec![],
            vec![(("src", false), ("image.png", true))],
        );
        meta_line.is_void = true;
        consumer.process_event(&Event::VoidElement { meta_line });

        let output = get_sexp_output(consumer);
        assert!(output.contains("(void type: \"img\""));
        assert!(output.contains("(kwarg"));
        assert!(output.contains("(key content: \"src\""));
        assert!(output.contains("(value content: \"image.png\" quoted: true"));
    }

    #[test]
    fn test_context_line_with_kwargs() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line(
            "@meta",
            1,
            "",
            false,
            "",
            vec![("value1", false)],
            vec![(("key1", false), ("val1", true))],
        );
        consumer.process_event(&Event::ContextLine { meta_line });

        let output = get_sexp_output(consumer);
        assert!(output.contains("(context-line type: \"@meta\""));
        assert!(output.contains("(arg content: \"value1\" quoted: false)"));
        assert!(output.contains("(kwarg"));
    }

    #[test]
    fn test_block_started_as_void() {
        // Test that BlockStarted with is_void=true is handled as void
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let mut meta_line = create_test_meta_line("hr", 1, "", false, "", vec![], vec![]);
        meta_line.is_void = true;
        consumer.process_event(&Event::BlockStarted { meta_line });
        // No BlockFinished needed for void elements

        let output = get_sexp_output(consumer);
        assert!(output.contains("(void type: \"hr\""));
    }

    #[test]
    fn test_balanced_parentheses_complex_document() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        // Create a complex document structure
        let outer = create_test_meta_line("document", 1, "Title", true, "doc-1", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line: outer });

        // Context line
        let ctx = create_test_meta_line("@author", 1, "John", false, "", vec![], vec![]);
        consumer.process_event(&Event::ContextLine { meta_line: ctx });

        // Nested section
        let section = create_test_meta_line("section", 2, "", false, "sec-1", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line: section });

        // Text with link
        let link = create_uri_link("click", "https://test.com", 0, 5);
        consumer.process_event(&Event::TextFinished {
            lines: vec!["click here".to_string()],
            links: vec![vec![link]],
            footnotes: vec![Footnote {
                marker: 1,
                link: "ref".to_string(),
            }],
        });

        consumer.process_event(&Event::BlockFinished); // section
        consumer.process_event(&Event::BlockFinished); // document

        let output = get_sexp_output(consumer);

        // Verify balanced parentheses
        let open_count = output.matches('(').count();
        let close_count = output.matches(')').count();
        assert_eq!(
            open_count, close_count,
            "Parentheses should be balanced in complex document"
        );
    }

    #[test]
    fn test_empty_text_lines() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        // Empty lines array should not produce text node
        consumer.process_event(&Event::TextFinished {
            lines: vec![],
            links: vec![],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        // Empty text should not be written
        assert!(!output.contains("(text"));
    }

    #[test]
    fn test_text_with_empty_string_lines() {
        let buffer = Vec::new();
        let mut consumer = ToSexpConsumer::new(buffer, true);

        let meta_line = create_test_meta_line("p", 1, "", false, "", vec![], vec![]);
        consumer.process_event(&Event::BlockStarted { meta_line });

        consumer.process_event(&Event::TextFinished {
            lines: vec!["".to_string(), "content".to_string(), "".to_string()],
            links: vec![vec![], vec![], vec![]],
            footnotes: vec![],
        });

        consumer.process_event(&Event::BlockFinished);

        let output = get_sexp_output(consumer);
        assert!(output.contains("(line index: 0 content: \"\")"));
        assert!(output.contains("(line index: 1 content: \"content\")"));
        assert!(output.contains("(line index: 2 content: \"\")"));
    }
}
