use crate::events::{Consumer, Event};
use crate::parsers::wasot::Block;
use std::borrow::Cow;
use std::io::Write;
use std::sync::{Arc, Mutex};
use xml::attribute::Attribute;
use xml::common::XmlVersion;
use xml::writer::{EventWriter, XmlEvent};

/// XML converter that listens to block events and generates XML output
pub struct ToXmlConsumer<W: Write> {
    writer: EventWriter<W>,
    element_stack: Vec<String>,
    indent_level: usize,
    pretty_print: bool,
    block_stack: Vec<crate::Block>,
}

impl<W: Write> ToXmlConsumer<W> {
    /// Create a new XML converter with the given writer
    pub fn new(writer: W, pretty_print: bool) -> Result<Self, xml::writer::Error> {
        let mut xml_writer = EventWriter::new(writer);

        // Write XML declaration
        xml_writer.write(XmlEvent::StartDocument {
            version: XmlVersion::Version10,
            encoding: Some("UTF-8"),
            standalone: None,
        })?;

        Ok(ToXmlConsumer {
            writer: xml_writer,
            element_stack: Vec::new(),
            indent_level: 0,
            pretty_print,
            block_stack: Vec::new(),
        })
    }

    /// Create block attributes (type, level, id, class)
    fn create_block_attributes(&self, block: &Block) -> Vec<(String, String)> {
        let mut attributes = Vec::new();

        // Required type attribute
        attributes.push(("type".to_string(), block.block_type().to_string()));

        // Required level attribute
        attributes.push(("level".to_string(), block.level().to_string()));

        // Optional id attribute - from the block's ID field
        if !block.id().value.is_empty() {
            attributes.push(("id".to_string(), block.id().value.clone()));
        }

        // Optional class attribute - extract from kwargs if present
        if let Some((_, class_value)) = block
            .arguments()
            .kwargs
            .iter()
            .find(|(key, _)| key.content == "class")
        {
            attributes.push(("class".to_string(), class_value.content.clone()));
        }

        attributes
    }

    /// Write pretty-printing whitespace
    fn write_indent(&mut self) -> Result<(), xml::writer::Error> {
        if self.pretty_print && self.indent_level > 0 {
            let indent = "\n".to_string() + &"  ".repeat(self.indent_level);
            self.writer.write(XmlEvent::Characters(&indent))?;
        }
        Ok(())
    }

    /// Start a new XML element for a block - write opening tag only
    fn start_element(&mut self, block: &Block) -> Result<(), xml::writer::Error> {
        let attributes = self.create_block_attributes(block);
        let empty_namespace = xml::namespace::Namespace::empty();

        // Write indentation before opening tag
        if self.pretty_print {
            self.write_indent()?;
        }

        // Create attributes in the format expected by xml-rs
        let xml_attributes: Vec<_> = attributes
            .iter()
            .map(|(name, value)| Attribute {
                name: xml::name::Name::local(name),
                value,
            })
            .collect();

        // Create the start element event for <block>
        let start_element = XmlEvent::StartElement {
            name: xml::name::Name::local("block"),
            attributes: Cow::Borrowed(&xml_attributes),
            namespace: Cow::Borrowed(&empty_namespace),
        };

        self.writer.write(start_element)?;

        // Track the element
        self.element_stack.push("block".to_string());
        self.indent_level += 1;

        Ok(())
    }

    /// Write the immediate content of a block (inline-text, args, kwargs, content) but not child blocks
    fn write_block_immediate_content(&mut self, block: &Block) -> Result<(), xml::writer::Error> {
        // Write inline text as <inline-text> element if present
        if !block.inline_text().content.is_empty() {
            self.write_element_with_text("inline-text", &block.inline_text().content)?;
        }

        // Write positional arguments as <arg> elements
        for arg in &block.arguments().args {
            self.write_element_with_text("arg", &arg.content)?;
        }

        // Write keyword arguments as <kwarg> elements (excluding class which is handled as attribute)
        for (key, value) in &block.arguments().kwargs {
            if key.content != "class" {
                self.write_kwarg_element(&key.content, &value.content)?;
            }
        }

        // Note: Text content is now handled through TextFinished events
        // rather than being written here from block.children
        // This maintains compatibility with the new event system design

        Ok(())
    }

    /// Helper to write a simple element with text content
    fn write_element_with_text(
        &mut self,
        element_name: &str,
        text_content: &str,
    ) -> Result<(), xml::writer::Error> {
        let empty_namespace = xml::namespace::Namespace::empty();

        if self.pretty_print {
            self.writer.write(XmlEvent::Characters("\n"))?;
            self.writer
                .write(XmlEvent::Characters(&"  ".repeat(self.indent_level)))?;
        }

        // Start element
        let start_element = XmlEvent::StartElement {
            name: xml::name::Name::local(element_name),
            attributes: Cow::Borrowed(&[]),
            namespace: Cow::Borrowed(&empty_namespace),
        };
        self.writer.write(start_element)?;

        // Write text content
        self.writer.write(XmlEvent::Characters(text_content))?;

        // End element
        self.writer.write(XmlEvent::EndElement {
            name: Some(xml::name::Name::local(element_name)),
        })?;

        Ok(())
    }

    /// Helper to write a kwarg element with nested key and value
    fn write_kwarg_element(&mut self, key: &str, value: &str) -> Result<(), xml::writer::Error> {
        let empty_namespace = xml::namespace::Namespace::empty();

        if self.pretty_print {
            self.writer.write(XmlEvent::Characters("\n"))?;
            self.writer
                .write(XmlEvent::Characters(&"  ".repeat(self.indent_level)))?;
        }

        // Start kwarg element
        let start_element = XmlEvent::StartElement {
            name: xml::name::Name::local("kwarg"),
            attributes: Cow::Borrowed(&[]),
            namespace: Cow::Borrowed(&empty_namespace),
        };
        self.writer.write(start_element)?;

        // Write key element
        self.indent_level += 1;
        self.write_element_with_text("key", key)?;

        // Write value element
        self.write_element_with_text("value", value)?;
        self.indent_level -= 1;

        // End kwarg element
        if self.pretty_print {
            self.writer.write(XmlEvent::Characters("\n"))?;
            self.writer
                .write(XmlEvent::Characters(&"  ".repeat(self.indent_level)))?;
        }

        self.writer.write(XmlEvent::EndElement {
            name: Some(xml::name::Name::local("kwarg")),
        })?;

        Ok(())
    }

    /// End the current XML element - write block content and closing tag
    fn end_element(&mut self, block: &Block) -> Result<(), xml::writer::Error> {
        if let Some(_element_name) = self.element_stack.pop() {
            // Write the block's immediate content before closing
            self.write_block_immediate_content(block)?;

            self.indent_level -= 1;

            // Write indentation before closing tag
            if self.pretty_print {
                self.writer.write(XmlEvent::Characters("\n"))?;
                self.writer
                    .write(XmlEvent::Characters(&"  ".repeat(self.indent_level)))?;
            }

            // Write the end element
            self.writer.write(XmlEvent::EndElement {
                name: Some(xml::name::Name::local("block")),
            })?;
        }

        Ok(())
    }

    /// Write a void element as a self-closing <block void="true" /> element
    fn write_void_element(&mut self, block: &Block) -> Result<(), xml::writer::Error> {
        let mut attributes = self.create_block_attributes(block);
        attributes.push(("void".to_string(), "true".to_string()));

        let empty_namespace = xml::namespace::Namespace::empty();

        // Write indentation before element
        if self.pretty_print {
            self.write_indent()?;
        }

        // Create attributes in the format expected by xml-rs
        let xml_attributes: Vec<_> = attributes
            .iter()
            .map(|(name, value)| Attribute {
                name: xml::name::Name::local(name),
                value,
            })
            .collect();

        // Create and write the self-closing element
        let start_element = XmlEvent::StartElement {
            name: xml::name::Name::local("block"),
            attributes: Cow::Borrowed(&xml_attributes),
            namespace: Cow::Borrowed(&empty_namespace),
        };
        self.writer.write(start_element)?;

        // Write the inline text and arguments inside the void element
        self.write_block_immediate_content(block)?;

        // Close the element
        self.writer.write(XmlEvent::EndElement {
            name: Some(xml::name::Name::local("block")),
        })?;

        Ok(())
    }

    /// Write a context line as a <context-line> element
    fn write_context_line(
        &mut self,
        meta_line: &crate::parsers::wasot::MetaLine,
    ) -> Result<(), xml::writer::Error> {
        use std::borrow::Cow;
        use xml::writer::XmlEvent;

        let empty_namespace = xml::namespace::Namespace::empty();

        // Build attributes
        let mut attrs: Vec<xml::attribute::OwnedAttribute> = vec![
            xml::attribute::OwnedAttribute {
                name: xml::name::OwnedName::local("type"),
                value: meta_line.type_name.clone(),
            },
            xml::attribute::OwnedAttribute {
                name: xml::name::OwnedName::local("level"),
                value: meta_line.level.to_string(),
            },
        ];

        // Add id attribute if present
        if !meta_line.id.value.is_empty() {
            attrs.push(xml::attribute::OwnedAttribute {
                name: xml::name::OwnedName::local("id"),
                value: meta_line.id.value.clone(),
            });
        }

        // Pretty print indentation
        if self.pretty_print {
            self.writer.write(XmlEvent::Characters("\n"))?;
            self.writer
                .write(XmlEvent::Characters(&"  ".repeat(self.indent_level)))?;
        }

        // Convert to borrowed attributes for the event
        let attr_refs: Vec<xml::attribute::Attribute> = attrs
            .iter()
            .map(|a| xml::attribute::Attribute {
                name: a.name.borrow(),
                value: &a.value,
            })
            .collect();

        // Start element
        let start_element = XmlEvent::StartElement {
            name: xml::name::Name::local("context-line"),
            attributes: Cow::Owned(attr_refs),
            namespace: Cow::Borrowed(&empty_namespace),
        };
        self.writer.write(start_element)?;

        // Write inline text as content if present
        if !meta_line.inline_text.content.is_empty() {
            self.writer
                .write(XmlEvent::Characters(&meta_line.inline_text.content))?;
        }

        // Write arguments as child elements if any
        if !meta_line.arguments.args.is_empty() || !meta_line.arguments.kwargs.is_empty() {
            self.indent_level += 1;

            // Write positional arguments
            for arg in &meta_line.arguments.args {
                self.write_element_with_text("arg", &arg.content)?;
            }

            // Write keyword arguments
            for (key, value) in &meta_line.arguments.kwargs {
                self.write_kwarg_element(&key.content, &value.content)?;
            }

            self.indent_level -= 1;

            // Pretty print closing indentation
            if self.pretty_print {
                self.writer.write(XmlEvent::Characters("\n"))?;
                self.writer
                    .write(XmlEvent::Characters(&"  ".repeat(self.indent_level)))?;
            }
        }

        // End element
        self.writer.write(XmlEvent::EndElement {
            name: Some(xml::name::Name::local("context-line")),
        })?;

        Ok(())
    }

    /// Finish writing the XML document
    pub fn finish(mut self) -> Result<W, xml::writer::Error> {
        // Add final newline for pretty printing
        if self.pretty_print {
            self.writer.write(XmlEvent::Characters("\n"))?;
        }

        Ok(self.writer.into_inner())
    }
}

impl<W: Write> ToXmlConsumer<W> {
    fn process_event(&mut self, event: &Event) {
        match event {
            Event::BlockStarted { meta_line, .. } => {
                // Create block from meta_line
                let block = Block::new(meta_line.clone());
                if let Err(e) = self.start_element(&block) {
                    eprintln!("XML conversion error on block start: {e}");
                }
                // Push block onto stack for later use in BlockFinished
                self.block_stack.push(block);
            }
            Event::BlockFinished => {
                // Pop the block from our stack (for structural consistency)
                if let Some(block) = self.block_stack.pop() {
                    // Use the block from our stack which has all content populated
                    if let Err(e) = self.end_element(&block) {
                        eprintln!("XML conversion error on block finish: {e}");
                    }
                } else {
                    eprintln!("Warning: BlockFinished event without matching BlockStarted");
                }
            }
            Event::TextFinished {
                lines, links: _, ..
            } => {
                // Write text content as <content> element
                if !lines.is_empty() {
                    let content_text = lines.join("\n");
                    if let Err(e) = self.write_element_with_text("content", &content_text) {
                        eprintln!("XML conversion error on text content: {e}");
                    }
                }
            }
            Event::ContextLine { meta_line } => {
                // Emit context line as a special <context-line> element
                if let Err(e) = self.write_context_line(meta_line) {
                    eprintln!("XML conversion error on context line: {e}");
                }
            }
            Event::VoidElement { meta_line } => {
                // Void elements are self-closing - write as <block void="true" />
                let block = Block::new(meta_line.clone());
                if let Err(e) = self.write_void_element(&block) {
                    eprintln!("XML conversion error on void element: {e}");
                }
            }
        }
    }
}

/// Consumer implementation for stdout output
impl Consumer for ToXmlConsumer<std::io::Stdout> {
    fn handle(&mut self, event: Event) {
        self.process_event(&event);
    }
}

/// Consumer implementation for arbitrary writers
impl<W: Write + Send + Sync + 'static> Consumer for Arc<Mutex<ToXmlConsumer<W>>> {
    fn handle(&mut self, event: Event) {
        // Note: This implementation is for Arc<Mutex<...>>, so &mut self is actually &mut Arc<Mutex<...>>
        // We need to lock the inner value to access it
        let mut converter = self.lock().unwrap();
        converter.process_event(&event);
    }
}

/// Convenience function to convert a Wasot string to XML string
/// NOTE: This function is currently commented out due to API changes.
/// It needs to be rewritten to work with the new Consumer trait approach.
#[allow(dead_code)]
pub fn convert_string_to_xml_string(
    _content: &str,
    _pretty_print: bool,
) -> Result<String, Box<dyn std::error::Error>> {
    // TODO: Reimplement this using the new Consumer/Emitter pattern
    // The previous implementation used DocumentParser which is deprecated
    Err("convert_string_to_xml_string is temporarily disabled during API migration".into())
}

/// Wrapper for shared writer access
pub struct SharedWriter {
    output: Arc<Mutex<Vec<u8>>>,
}

impl SharedWriter {
    #[allow(dead_code)]
    pub fn new(output: Arc<Mutex<Vec<u8>>>) -> Self {
        SharedWriter { output }
    }
}

impl Write for SharedWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let mut output = self.output.lock().unwrap();
        output.extend_from_slice(buf);
        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

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

    // Helper function to create test MetaLine
    fn create_test_meta_line(
        block_type: &str,
        level: usize,
        inline_text: &str,
        id: &str,
        args: Vec<&str>,
        kwargs: Vec<(&str, &str)>,
    ) -> MetaLine {
        let args: Vec<QuotedString> = args
            .into_iter()
            .map(|s| QuotedString::new(s.to_string(), false))
            .collect();
        let kwargs: Vec<(QuotedString, QuotedString)> = kwargs
            .into_iter()
            .map(|(k, v)| {
                (
                    QuotedString::new(k.to_string(), false),
                    QuotedString::new(v.to_string(), false),
                )
            })
            .collect();

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

    // Helper function to create test Block
    fn create_test_block(
        block_type: &str,
        level: usize,
        inline_text: &str,
        id: &str,
        args: Vec<&str>,
        kwargs: Vec<(&str, &str)>,
    ) -> Block {
        let meta_line = create_test_meta_line(block_type, level, inline_text, id, args, kwargs);
        Block::new(meta_line)
    }

    // Helper to get XML output as string
    fn get_xml_output(consumer: ToXmlConsumer<Vec<u8>>) -> String {
        let xml_bytes = consumer.finish().unwrap();
        String::from_utf8(xml_bytes).unwrap()
    }

    // Tests for ToXmlConsumer::new

    #[test]
    fn test_new_without_pretty_print() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, false);
        assert!(consumer.is_ok());

        let consumer = consumer.unwrap();
        assert!(!consumer.pretty_print);
        assert_eq!(consumer.indent_level, 0);
        assert!(consumer.element_stack.is_empty());
        assert!(consumer.block_stack.is_empty());
    }

    #[test]
    fn test_new_with_pretty_print() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, true);
        assert!(consumer.is_ok());

        let consumer = consumer.unwrap();
        assert!(consumer.pretty_print);
        assert_eq!(consumer.indent_level, 0);
        assert!(consumer.element_stack.is_empty());
        assert!(consumer.block_stack.is_empty());
    }

    #[test]
    fn test_new_includes_xml_declaration() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let xml_output = get_xml_output(consumer);
        assert!(xml_output.starts_with(r#"<?xml version="1.0" encoding="UTF-8"?>"#));
    }

    // Tests for create_block_attributes

    #[test]
    fn test_create_block_attributes_basic() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("paragraph", 1, "", "", vec![], vec![]);

        let attributes = consumer.create_block_attributes(&block);

        assert_eq!(attributes.len(), 2);
        assert!(attributes.contains(&("type".to_string(), "paragraph".to_string())));
        assert!(attributes.contains(&("level".to_string(), "1".to_string())));
    }

    #[test]
    fn test_create_block_attributes_with_id() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("section", 2, "", "my-id", vec![], vec![]);

        let attributes = consumer.create_block_attributes(&block);

        assert_eq!(attributes.len(), 3);
        assert!(attributes.contains(&("type".to_string(), "section".to_string())));
        assert!(attributes.contains(&("level".to_string(), "2".to_string())));
        assert!(attributes.contains(&("id".to_string(), "my-id".to_string())));
    }

    #[test]
    fn test_create_block_attributes_with_class() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block(
            "div",
            1,
            "",
            "",
            vec![],
            vec![("class", "highlight"), ("other", "value")],
        );

        let attributes = consumer.create_block_attributes(&block);

        assert!(attributes.contains(&("type".to_string(), "div".to_string())));
        assert!(attributes.contains(&("level".to_string(), "1".to_string())));
        assert!(attributes.contains(&("class".to_string(), "highlight".to_string())));
        // Should not contain other kwargs as attributes
        assert!(!attributes.iter().any(|(k, _)| k == "other" || k == "value"));
    }

    #[test]
    fn test_create_block_attributes_all_features() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block(
            "article",
            3,
            "",
            "article-1",
            vec![],
            vec![("class", "featured")],
        );

        let attributes = consumer.create_block_attributes(&block);

        assert_eq!(attributes.len(), 4);
        assert!(attributes.contains(&("type".to_string(), "article".to_string())));
        assert!(attributes.contains(&("level".to_string(), "3".to_string())));
        assert!(attributes.contains(&("id".to_string(), "article-1".to_string())));
        assert!(attributes.contains(&("class".to_string(), "featured".to_string())));
    }

    // Tests for write_indent

    #[test]
    fn test_write_indent_without_pretty_print() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        consumer.indent_level = 2;

        let result = consumer.write_indent();
        assert!(result.is_ok());

        // Should not write anything when pretty_print is false
        let xml_output = get_xml_output(consumer);
        assert_eq!(xml_output, r#"<?xml version="1.0" encoding="UTF-8"?>"#);
    }

    #[test]
    fn test_write_indent_with_pretty_print() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, true).unwrap();
        consumer.indent_level = 2;

        let result = consumer.write_indent();
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("\n    ")); // 2 levels * 2 spaces
    }

    #[test]
    fn test_write_indent_zero_level() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, true).unwrap();
        consumer.indent_level = 0;

        let result = consumer.write_indent();
        assert!(result.is_ok());

        // Should not write indent when level is 0, but may have XML declaration with newline
        let xml_output = get_xml_output(consumer);
        assert!(xml_output.starts_with(r#"<?xml version="1.0" encoding="UTF-8"?>"#));
    }

    // Tests for start_element

    #[test]
    fn test_start_element_basic() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("paragraph", 1, "", "", vec![], vec![]);

        let result = consumer.start_element(&block);
        assert!(result.is_ok());

        assert_eq!(consumer.element_stack.len(), 1);
        assert_eq!(consumer.element_stack[0], "block");
        assert_eq!(consumer.indent_level, 1);

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains(r#"<block type="paragraph" level="1""#));
    }

    #[test]
    fn test_start_element_with_attributes() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("section", 2, "", "test-id", vec![], vec![("class", "main")]);

        let result = consumer.start_element(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains(r#"<block type="section" level="2" id="test-id" class="main""#));
    }

    #[test]
    fn test_start_element_with_pretty_print() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, true).unwrap();
        let block = create_test_block("div", 1, "", "", vec![], vec![]);

        let result = consumer.start_element(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        // Should contain indentation
        assert!(xml_output.contains("\n"));
    }

    // Tests for write_element_with_text

    #[test]
    fn test_write_element_with_text_basic() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

        let result = consumer.write_element_with_text("test", "content");
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<test>content</test>"));
    }

    #[test]
    fn test_write_element_with_text_with_pretty_print() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, true).unwrap();
        consumer.indent_level = 1;

        let result = consumer.write_element_with_text("example", "test content");
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<example>test content</example>"));
        assert!(xml_output.contains("\n  ")); // Indentation
    }

    #[test]
    fn test_write_element_with_text_empty_content() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

        let result = consumer.write_element_with_text("empty", "");
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<empty></empty>"));
    }

    // Tests for write_kwarg_element

    #[test]
    fn test_write_kwarg_element_basic() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

        let result = consumer.write_kwarg_element("theme", "dark");
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<kwarg>"));
        assert!(xml_output.contains("<key>theme</key>"));
        assert!(xml_output.contains("<value>dark</value>"));
        assert!(xml_output.contains("</kwarg>"));
    }

    #[test]
    fn test_write_kwarg_element_with_pretty_print() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, true).unwrap();
        consumer.indent_level = 1;

        let result = consumer.write_kwarg_element("size", "large");
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<kwarg>"));
        assert!(xml_output.contains("<key>size</key>"));
        assert!(xml_output.contains("<value>large</value>"));
        assert!(xml_output.contains("</kwarg>"));
        // Should contain proper indentation
        assert!(xml_output.contains("\n  "));
    }

    // Tests for write_block_immediate_content

    #[test]
    fn test_write_block_immediate_content_inline_text() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("p", 1, "Some inline text", "", vec![], vec![]);

        let result = consumer.write_block_immediate_content(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<inline-text>Some inline text</inline-text>"));
    }

    #[test]
    fn test_write_block_immediate_content_args() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("div", 1, "", "", vec!["arg1", "arg2"], vec![]);

        let result = consumer.write_block_immediate_content(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<arg>arg1</arg>"));
        assert!(xml_output.contains("<arg>arg2</arg>"));
    }

    #[test]
    fn test_write_block_immediate_content_kwargs() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block(
            "section",
            1,
            "",
            "",
            vec![],
            vec![("theme", "dark"), ("size", "large")],
        );

        let result = consumer.write_block_immediate_content(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<kwarg>"));
        assert!(xml_output.contains("<key>theme</key>"));
        assert!(xml_output.contains("<value>dark</value>"));
        assert!(xml_output.contains("<key>size</key>"));
        assert!(xml_output.contains("<value>large</value>"));
    }

    #[test]
    fn test_write_block_immediate_content_excludes_class_kwarg() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block(
            "div",
            1,
            "",
            "",
            vec![],
            vec![("class", "highlight"), ("other", "value")],
        );

        let result = consumer.write_block_immediate_content(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        // Should contain other kwargs but not class
        assert!(xml_output.contains("<key>other</key>"));
        assert!(xml_output.contains("<value>value</value>"));
        // Should NOT contain class kwarg (it's handled as attribute)
        assert!(!xml_output.contains("<key>class</key>"));
    }

    #[test]
    fn test_write_block_immediate_content_all_features() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block(
            "article",
            1,
            "Inline content",
            "",
            vec!["arg1"],
            vec![("theme", "light")],
        );

        let result = consumer.write_block_immediate_content(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<inline-text>Inline content</inline-text>"));
        assert!(xml_output.contains("<arg>arg1</arg>"));
        assert!(xml_output.contains("<key>theme</key>"));
        assert!(xml_output.contains("<value>light</value>"));
    }

    // Tests for end_element

    #[test]
    fn test_end_element_basic() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("p", 1, "", "", vec![], vec![]);

        // First properly start the element
        let start_result = consumer.start_element(&block);
        assert!(start_result.is_ok());

        // Verify state after start
        assert_eq!(consumer.element_stack.len(), 1);
        assert_eq!(consumer.indent_level, 1);

        let result = consumer.end_element(&block);
        assert!(result.is_ok());

        // Verify state after end
        assert!(consumer.element_stack.is_empty());
        assert_eq!(consumer.indent_level, 0);

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains(r#"<block type="p" level="1""#));
        // XML writer creates self-closing tag when there's no content
        assert!(xml_output.contains("/>"));
    }

    #[test]
    fn test_end_element_with_content() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("div", 1, "Inline text", "", vec!["arg"], vec![]);

        // First properly start the element
        let start_result = consumer.start_element(&block);
        assert!(start_result.is_ok());

        let result = consumer.end_element(&block);
        assert!(result.is_ok());

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<inline-text>Inline text</inline-text>"));
        assert!(xml_output.contains("<arg>arg</arg>"));
        assert!(xml_output.contains("</block>"));
    }

    #[test]
    fn test_end_element_empty_stack() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let block = create_test_block("p", 1, "", "", vec![], vec![]);

        // Element stack is empty - should handle gracefully
        let result = consumer.end_element(&block);
        assert!(result.is_ok());
    }

    // Tests for finish

    #[test]
    fn test_finish_without_pretty_print() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let result = consumer.finish();
        assert!(result.is_ok());
    }

    #[test]
    fn test_finish_with_pretty_print() {
        let buffer = Vec::new();
        let consumer = ToXmlConsumer::new(buffer, true).unwrap();
        let xml_bytes = consumer.finish().unwrap();
        let xml_output = String::from_utf8(xml_bytes).unwrap();
        // Should end with newline for pretty printing
        assert!(xml_output.ends_with('\n'));
    }

    // Tests for process_event

    #[test]
    fn test_process_event_block_started() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();
        let meta_line = create_test_meta_line("paragraph", 1, "", "", vec![], vec![]);
        let event = Event::BlockStarted { meta_line };

        assert_eq!(consumer.block_stack.len(), 0);
        assert_eq!(consumer.element_stack.len(), 0);

        consumer.process_event(&event);

        assert_eq!(consumer.block_stack.len(), 1);
        assert_eq!(consumer.element_stack.len(), 1);
        assert_eq!(consumer.indent_level, 1);
    }

    #[test]
    fn test_process_event_block_finished() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

        // First start a block
        let meta_line = create_test_meta_line("div", 1, "", "", vec![], vec![]);
        let start_event = Event::BlockStarted { meta_line };
        consumer.process_event(&start_event);

        assert_eq!(consumer.block_stack.len(), 1);
        assert_eq!(consumer.element_stack.len(), 1);

        // Then finish it
        let finish_event = Event::BlockFinished;
        consumer.process_event(&finish_event);

        assert_eq!(consumer.block_stack.len(), 0);
        assert_eq!(consumer.element_stack.len(), 0);
        assert_eq!(consumer.indent_level, 0);

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains(r#"<block type="div" level="1""#));
        // XML writer creates self-closing tag when there's no content
        assert!(xml_output.contains("/>"));
    }

    #[test]
    fn test_process_event_block_finished_no_matching_start() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

        // Try to finish without starting
        let finish_event = Event::BlockFinished;
        consumer.process_event(&finish_event);

        // Should handle gracefully
        assert_eq!(consumer.block_stack.len(), 0);
        assert_eq!(consumer.element_stack.len(), 0);
    }

    #[test]
    fn test_process_event_text_finished() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

        let text_event = Event::TextFinished {
            lines: vec!["Line 1".to_string(), "Line 2".to_string()],
            links: vec![vec![], vec![]],
            footnotes: vec![],
        };

        consumer.process_event(&text_event);

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains("<content>Line 1\nLine 2</content>"));
    }

    #[test]
    fn test_process_event_text_finished_empty() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

        let text_event = Event::TextFinished {
            lines: vec![],
            links: vec![],
            footnotes: vec![],
        };

        consumer.process_event(&text_event);

        let xml_output = get_xml_output(consumer);
        // Should not contain content element for empty text
        assert!(!xml_output.contains("<content>"));
    }

    // Tests for Consumer trait implementation

    #[test]
    fn test_consumer_handle_stdout() {
        // This test is harder to verify since it writes to stdout
        // Just ensure it doesn't panic
        let mut consumer = ToXmlConsumer::new(std::io::stdout(), false).unwrap();
        let meta_line = create_test_meta_line("test", 1, "", "", vec![], vec![]);
        let event = Event::BlockStarted { meta_line };

        consumer.handle(event); // Should not panic
    }

    // Tests for convert_string_to_xml_string

    #[test]
    fn test_convert_string_to_xml_string_disabled() {
        let result = convert_string_to_xml_string("test", true);
        assert!(result.is_err());
        let error_msg = result.err().unwrap().to_string();
        assert!(error_msg.contains("temporarily disabled"));
    }

    // Tests for SharedWriter

    #[test]
    fn test_shared_writer_new() {
        let output = Arc::new(Mutex::new(Vec::new()));
        let writer = SharedWriter::new(output);
        // Should create successfully
        let _ = writer;
    }

    #[test]
    fn test_shared_writer_write() {
        let output = Arc::new(Mutex::new(Vec::new()));
        let mut writer = SharedWriter::new(output.clone());

        let data = b"test data";
        let result = writer.write(data);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), data.len());

        // Check that data was written to the shared buffer
        let buffer = output.lock().unwrap();
        assert_eq!(*buffer, data);
    }

    #[test]
    fn test_shared_writer_flush() {
        let output = Arc::new(Mutex::new(Vec::new()));
        let mut writer = SharedWriter::new(output);

        let result = writer.flush();
        assert!(result.is_ok());
    }

    // Integration tests

    #[test]
    fn test_full_workflow() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, true).unwrap();

        // Process a complete block workflow
        let meta_line = create_test_meta_line(
            "article",
            1,
            "Header text",
            "art-1",
            vec!["arg1"],
            vec![("theme", "dark")],
        );
        let start_event = Event::BlockStarted { meta_line };
        consumer.process_event(&start_event);

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

        let finish_event = Event::BlockFinished;
        consumer.process_event(&finish_event);

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains(r#"<block type="article" level="1" id="art-1""#));
        assert!(xml_output.contains("<inline-text>Header text</inline-text>"));
        assert!(xml_output.contains("<arg>arg1</arg>"));
        assert!(xml_output.contains("<key>theme</key>"));
        assert!(xml_output.contains("<value>dark</value>"));
        assert!(xml_output.contains("<content>Article content</content>"));
        assert!(xml_output.contains("</block>"));
    }

    #[test]
    fn test_nested_blocks() {
        let buffer = Vec::new();
        let mut consumer = ToXmlConsumer::new(buffer, false).unwrap();

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

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

        // Add text to inner block
        consumer.process_event(&Event::TextFinished {
            lines: vec!["Inner content".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        });

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

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

        let xml_output = get_xml_output(consumer);
        assert!(xml_output.contains(r#"<block type="section" level="1""#));
        assert!(xml_output.contains(r#"<block type="paragraph" level="2""#));
        assert!(xml_output.contains("<content>Inner content</content>"));
        // Should have properly nested structure
        let block_count = xml_output.matches("<block").count();
        let end_block_count = xml_output.matches("</block>").count();
        assert_eq!(block_count, end_block_count);
    }
}
