//! Event interceptor system for processing events before they are emitted
//!
//! This module provides a flexible interceptor pattern that allows for
//! modular processing of events before they are broadcast to consumers.

use crate::events::Event;
use crate::parsers::wasot::{Footnote, Link};

/// Trait for interceptors that can modify events before they are emitted
pub trait EventInterceptor: Send + Sync {
    /// Process an event and potentially modify it
    /// Returns the modified event (or the original if no changes)
    fn intercept(&mut self, event: Event) -> Event;

    /// Called when the interceptor is initialized
    fn initialize(&mut self) {}

    /// Called when processing is finished
    fn finalize(&mut self) {}
}

/// Chain of interceptors that can be applied to events
pub struct InterceptorChain {
    interceptors: Vec<Box<dyn EventInterceptor>>,
}

impl InterceptorChain {
    /// Create a new empty interceptor chain
    pub fn new() -> Self {
        Self {
            interceptors: Vec::new(),
        }
    }

    /// Add an interceptor to the chain
    pub fn add_interceptor<T: EventInterceptor + 'static>(mut self, interceptor: T) -> Self {
        self.interceptors.push(Box::new(interceptor));
        self
    }

    /// Initialize all interceptors in the chain
    pub fn initialize(&mut self) {
        for interceptor in &mut self.interceptors {
            interceptor.initialize();
        }
    }

    /// Process an event through the entire interceptor chain
    pub fn process(&mut self, mut event: Event) -> Event {
        for interceptor in &mut self.interceptors {
            event = interceptor.intercept(event);
        }
        event
    }

    /// Finalize all interceptors in the chain
    pub fn finalize(&mut self) {
        for interceptor in &mut self.interceptors {
            interceptor.finalize();
        }
    }
}

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

/// Interceptor that converts inline links to footnotes in TextFinished events
pub struct FootnoteInterceptor {
    /// Whether to process URI links (a-z://)
    process_uri_links: bool,
}

impl FootnoteInterceptor {
    /// Create a new footnote interceptor
    pub fn new() -> Self {
        Self {
            process_uri_links: true,
        }
    }

    /// Check if a target string is a URI (matches pattern a-z://)
    fn is_uri_link(&self, target: &str) -> bool {
        if !self.process_uri_links {
            return false;
        }

        // Minimum length for "a://" is 4 characters
        if target.len() < 4 {
            return false;
        }

        // Get the first character - must be a-z
        let first_byte = target.as_bytes()[0];
        if !first_byte.is_ascii_lowercase() {
            return false;
        }

        // Check for "://" pattern after the first character
        // Using byte slice to avoid allocation
        target[1..].contains("://")
    }

    /// Convert links to footnotes in a set of text lines
    fn convert_links_to_footnotes(
        &mut self,
        lines: Vec<String>,
        existing_links: Vec<Vec<Link>>,
        existing_footnotes: &[Footnote],
    ) -> (Vec<String>, Vec<Vec<Link>>, Vec<Footnote>) {
        use crate::parsers::wasot::{detect_links, parse_link};

        // Pre-allocate output vectors to avoid reallocations
        let num_lines = lines.len();
        let mut processed_lines = Vec::with_capacity(num_lines);
        let mut processed_links = Vec::with_capacity(num_lines);
        let mut generated_footnotes = Vec::new();
        // Find the highest existing footnote marker to continue numbering
        let max_existing_marker = existing_footnotes
            .iter()
            .map(|f| f.marker)
            .max()
            .unwrap_or(0);
        let mut local_footnote_counter = max_existing_marker + 1;

        for (line, line_links) in lines.into_iter().zip(existing_links.into_iter()) {
            let spans = detect_links(&line);

            if spans.is_empty() {
                // No links in this line, keep as-is
                processed_lines.push(line);
                processed_links.push(line_links);
            } else {
                // Process links in this line
                let mut processed_line = line;
                let mut processed_line_links = Vec::new();
                let mut offset = 0i32; // Track character offset changes due to replacements

                for span in spans {
                    let link = parse_link(&span.content, span.start_index, span.length);

                    // Only convert URI links, keep others as-is
                    if let Some(target) = &link.target {
                        if self.is_uri_link(target) {
                            // Reconstruct the link with footnote target
                            // Pre-allocate with estimated capacity to reduce reallocations
                            let mut new_link_content = String::with_capacity(64);

                            // Add name if present
                            if let Some(name) = &link.name {
                                new_link_content.push_str(&name.content);
                            }

                            // Add arguments if present
                            if !link.arguments.args.is_empty() || !link.arguments.kwargs.is_empty()
                            {
                                new_link_content.push_str(" ..");
                                for arg in &link.arguments.args {
                                    new_link_content.push(' ');
                                    if arg.quoted {
                                        new_link_content.push('"');
                                        new_link_content.push_str(&arg.content);
                                        new_link_content.push('"');
                                    } else {
                                        new_link_content.push_str(&arg.content);
                                    }
                                }
                                for (key, value) in &link.arguments.kwargs {
                                    new_link_content.push(' ');
                                    new_link_content.push_str(&key.content);
                                    new_link_content.push('=');
                                    if value.quoted {
                                        new_link_content.push('"');
                                        new_link_content.push_str(&value.content);
                                        new_link_content.push('"');
                                    } else {
                                        new_link_content.push_str(&value.content);
                                    }
                                }
                            }

                            // Add footnote target - avoid format! allocation
                            new_link_content.push_str(" :*");
                            // Use itoa for efficient integer formatting
                            use std::fmt::Write;
                            let _ = write!(new_link_content, "{local_footnote_counter}");

                            // Build replacement string - avoid format! allocation
                            let mut replacement = String::with_capacity(new_link_content.len() + 4);
                            replacement.push_str("((");
                            replacement.push_str(&new_link_content);
                            replacement.push_str("))");

                            // Calculate adjusted positions based on previous replacements
                            let start_pos = (span.start_index as i32 + offset) as usize;
                            let end_pos = start_pos + span.length;

                            // Replace the entire link with the reconstructed one
                            if start_pos <= processed_line.len() && end_pos <= processed_line.len()
                            {
                                processed_line.replace_range(start_pos..end_pos, &replacement);

                                // Update offset for next replacements
                                offset += replacement.len() as i32 - span.length as i32;
                            }

                            // Create footnote entry
                            generated_footnotes.push(Footnote {
                                marker: local_footnote_counter,
                                link: target.clone(),
                            });

                            local_footnote_counter += 1;
                        } else {
                            // Keep non-URI links as-is
                            processed_line_links.push(link);
                        }
                    } else {
                        // Keep links without targets as-is
                        processed_line_links.push(link);
                    }
                }

                processed_lines.push(processed_line);
                processed_links.push(processed_line_links);
            }
        }

        (processed_lines, processed_links, generated_footnotes)
    }
}

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

impl EventInterceptor for FootnoteInterceptor {
    fn intercept(&mut self, event: Event) -> Event {
        match event {
            Event::TextFinished {
                lines,
                links,
                mut footnotes,
            } => {
                // Convert URI links to footnotes
                let (processed_lines, processed_links, mut generated_footnotes) =
                    self.convert_links_to_footnotes(lines, links, &footnotes);

                // Add generated footnotes to existing ones
                footnotes.append(&mut generated_footnotes);

                Event::TextFinished {
                    lines: processed_lines,
                    links: processed_links,
                    footnotes,
                }
            }
            other => other, // Pass through other events unchanged
        }
    }
}

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

    #[test]
    fn test_footnote_interceptor_uri_detection() {
        let interceptor = FootnoteInterceptor::new();

        // Should match
        assert!(interceptor.is_uri_link("https://example.com"));
        assert!(interceptor.is_uri_link("http://example.com"));
        assert!(interceptor.is_uri_link("ftp://files.example.com"));
        assert!(interceptor.is_uri_link("git://github.com/user/repo"));
        assert!(interceptor.is_uri_link("a://example.com"));

        // Should not match
        assert!(!interceptor.is_uri_link("HTTP://example.com")); // uppercase
        assert!(!interceptor.is_uri_link("123://example.com")); // starts with number
        assert!(!interceptor.is_uri_link("section1")); // no protocol
        assert!(!interceptor.is_uri_link("*1")); // footnote reference
        assert!(!interceptor.is_uri_link("")); // empty
    }

    #[test]
    fn test_link_text_preservation() {
        let mut chain = InterceptorChain::new().add_interceptor(FootnoteInterceptor::new());

        chain.initialize();

        // Test that link text and arguments are preserved, only target is replaced
        let event = Event::TextFinished {
            lines: vec!["Visit ((our website :https://example.com)) for more info.".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        };

        let processed_event = chain.process(event);

        if let Event::TextFinished {
            lines, footnotes, ..
        } = processed_event
        {
            assert_eq!(lines.len(), 1);
            // Should preserve the link text and only replace the target
            assert_eq!(lines[0], "Visit ((our website :*1)) for more info.");
            assert_eq!(footnotes.len(), 1);
            assert_eq!(footnotes[0].marker, 1);
            assert_eq!(footnotes[0].link, "https://example.com");
        } else {
            panic!("Expected TextFinished event");
        }

        chain.finalize();
    }

    #[test]
    fn test_link_with_arguments_preservation() {
        let mut chain = InterceptorChain::new().add_interceptor(FootnoteInterceptor::new());

        chain.initialize();

        // Test that link arguments are preserved
        let event = Event::TextFinished {
            lines: vec![
                "See ((API docs .. version=v2 format=json :https://api.example.com)) here."
                    .to_string(),
            ],
            links: vec![vec![]],
            footnotes: vec![],
        };

        let processed_event = chain.process(event);

        if let Event::TextFinished {
            lines, footnotes, ..
        } = processed_event
        {
            assert_eq!(lines.len(), 1);
            // Should preserve the link text and arguments, only replace the target
            assert_eq!(
                lines[0],
                "See ((API docs .. version=v2 format=json :*1)) here."
            );
            assert_eq!(footnotes.len(), 1);
            assert_eq!(footnotes[0].marker, 1);
            assert_eq!(footnotes[0].link, "https://api.example.com");
        } else {
            panic!("Expected TextFinished event");
        }

        chain.finalize();
    }

    #[test]
    fn test_interceptor_chain() {
        let mut chain = InterceptorChain::new().add_interceptor(FootnoteInterceptor::new());

        chain.initialize();

        // Test processing a TextFinished event
        let event = Event::TextFinished {
            lines: vec!["Visit ((our website :https://example.com)) for more info.".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        };

        let processed_event = chain.process(event);

        if let Event::TextFinished {
            lines, footnotes, ..
        } = processed_event
        {
            assert_eq!(lines.len(), 1);
            assert!(lines[0].contains("*1"));
            assert_eq!(footnotes.len(), 1);
            assert_eq!(footnotes[0].marker, 1);
            assert_eq!(footnotes[0].link, "https://example.com");
        } else {
            panic!("Expected TextFinished event");
        }

        chain.finalize();
    }

    // Additional tests to ensure complete coverage of specific lines mentioned in TODO

    // Mock interceptor for testing basic EventInterceptor trait
    struct MockInterceptor {
        initialized: bool,
        finalized: bool,
        processed_count: usize,
    }

    impl MockInterceptor {
        fn new() -> Self {
            Self {
                initialized: false,
                finalized: false,
                processed_count: 0,
            }
        }
    }

    impl EventInterceptor for MockInterceptor {
        fn intercept(&mut self, event: Event) -> Event {
            self.processed_count += 1;
            event
        }

        fn initialize(&mut self) {
            // Test line 16: fn initialize()
            self.initialized = true;
        }

        fn finalize(&mut self) {
            // Test line 19: fn finalize()
            self.finalized = true;
        }
    }

    #[test]
    fn test_event_interceptor_initialize() {
        // Test line 16 in function `initialize`
        let mut mock = MockInterceptor::new();
        assert!(!mock.initialized);

        mock.initialize();
        assert!(mock.initialized);
    }

    #[test]
    fn test_event_interceptor_finalize() {
        // Test line 19 in function `finalize`
        let mut mock = MockInterceptor::new();
        assert!(!mock.finalized);

        mock.finalize();
        assert!(mock.finalized);
    }

    #[test]
    fn test_interceptor_chain_new() {
        // Test line 29 in function `new`
        let chain = InterceptorChain::new();
        assert_eq!(chain.interceptors.len(), 0);
    }

    #[test]
    fn test_interceptor_chain_new_vec_initialization() {
        // Test line 31 in function `new` - interceptors: Vec::new()
        let chain = InterceptorChain::new();
        assert!(chain.interceptors.is_empty());
    }

    #[test]
    fn test_add_interceptor() {
        // Test lines 36-38 in function `add_interceptor`
        let chain = InterceptorChain::new();
        assert_eq!(chain.interceptors.len(), 0);

        let chain = chain.add_interceptor(MockInterceptor::new());
        assert_eq!(chain.interceptors.len(), 1);

        let chain = chain.add_interceptor(MockInterceptor::new());
        assert_eq!(chain.interceptors.len(), 2);
    }

    #[test]
    fn test_interceptor_chain_initialize() {
        // Test lines 42-44 in function `initialize`
        let mut chain = InterceptorChain::new().add_interceptor(MockInterceptor::new());

        // Initialize the chain
        chain.initialize();

        // We can't directly access the internal state, but we can verify the method runs without error
        // The MockInterceptor's initialize method should have been called
    }

    #[test]
    fn test_interceptor_chain_process() {
        // Test lines 49-51 in function `process`
        // Test line 53 in function `process` - event return
        use crate::events::Event;

        let mut chain = InterceptorChain::new().add_interceptor(MockInterceptor::new());

        let event = Event::BlockStarted {
            meta_line: crate::parsers::wasot::MetaLine {
                type_name: "test".to_string(),
                level: 1,
                id: crate::parsers::wasot::Id::new("test_id".to_string()),
                arguments: crate::parsers::wasot::ParsedArguments {
                    args: vec![],
                    kwargs: vec![],
                },
                inline_text: crate::parsers::wasot::QuotedString::new("".to_string(), false),
                line_index: Some(0),
                is_void: false,
            },
        };

        let processed_event = chain.process(event);

        // Should return the same event (MockInterceptor doesn't modify it)
        if let Event::BlockStarted { meta_line } = processed_event {
            assert_eq!(meta_line.type_name, "test");
        } else {
            panic!("Expected BlockStarted event");
        }
    }

    #[test]
    fn test_interceptor_chain_finalize() {
        // Test lines 57-59 in function `finalize`
        let mut chain = InterceptorChain::new().add_interceptor(MockInterceptor::new());

        // Finalize the chain
        chain.finalize();

        // We can't directly access the internal state, but we can verify the method runs without error
        // The MockInterceptor's finalize method should have been called
    }

    #[test]
    fn test_interceptor_chain_default() {
        // Test lines 65-66 in function `default`
        let chain: InterceptorChain = Default::default();
        assert_eq!(chain.interceptors.len(), 0);
    }

    #[test]
    fn test_footnote_interceptor_new() {
        let interceptor = FootnoteInterceptor::new();
        assert!(interceptor.process_uri_links);
    }

    #[test]
    fn test_footnote_interceptor_default() {
        let interceptor: FootnoteInterceptor = Default::default();
        assert!(interceptor.process_uri_links);
    }

    #[test]
    fn test_is_uri_link_process_disabled() {
        // Test lines 85-87 in function `is_uri_link`
        let mut interceptor = FootnoteInterceptor::new();
        interceptor.process_uri_links = false;

        // Should return false even for valid URIs when processing is disabled
        assert!(!interceptor.is_uri_link("https://example.com"));
        assert!(!interceptor.is_uri_link("http://test.com"));
    }

    #[test]
    fn test_is_uri_link_length_check() {
        // Test lines 90-91 in function `is_uri_link`
        let interceptor = FootnoteInterceptor::new();

        // Should return false for strings shorter than 4 characters
        assert!(!interceptor.is_uri_link(""));
        assert!(!interceptor.is_uri_link("a"));
        assert!(!interceptor.is_uri_link("ab"));
        assert!(!interceptor.is_uri_link("abc"));

        // Should work for strings 4+ characters if they match pattern
        assert!(interceptor.is_uri_link("a://"));
    }

    #[test]
    fn test_is_uri_link_first_char_check() {
        // Test lines 93-94 in function `is_uri_link`
        let interceptor = FootnoteInterceptor::new();

        // Should return false if first character is not lowercase
        assert!(!interceptor.is_uri_link("HTTP://example.com"));
        assert!(!interceptor.is_uri_link("1://example.com"));
        assert!(!interceptor.is_uri_link("://example.com"));

        // Should work if first character is lowercase
        assert!(interceptor.is_uri_link("http://example.com"));
        assert!(interceptor.is_uri_link("a://example.com"));
    }

    #[test]
    fn test_is_uri_link_protocol_pattern() {
        // Test lines 96-97 in function `is_uri_link`
        let interceptor = FootnoteInterceptor::new();

        // Should return false if doesn't contain ://
        assert!(!interceptor.is_uri_link("httpexample.com"));
        assert!(!interceptor.is_uri_link("http:example.com"));
        assert!(!interceptor.is_uri_link("http/example.com"));

        // Should return true if contains ://
        assert!(interceptor.is_uri_link("http://example.com"));
        assert!(interceptor.is_uri_link("ftp://files.com"));
    }

    #[test]
    fn test_is_uri_link_false_branch() {
        // Test line 99 in function `is_uri_link` - else false branch
        let interceptor = FootnoteInterceptor::new();

        // Test case where first char is not lowercase
        assert!(!interceptor.is_uri_link("HTTP://example.com"));
    }

    #[test]
    fn test_is_uri_link_empty_none_case() {
        // Test line 102 in function `is_uri_link` - else false branch for None
        let interceptor = FootnoteInterceptor::new();

        // Empty string should return false
        assert!(!interceptor.is_uri_link(""));
    }

    #[test]
    fn test_convert_links_to_footnotes_vec_initialization() {
        // Test line 108 in function `convert_links_to_footnotes`
        let mut interceptor = FootnoteInterceptor::new();

        let (processed_lines, processed_links, generated_footnotes) =
            interceptor.convert_links_to_footnotes(vec![], vec![], &[]);

        // Should initialize empty vectors
        assert!(processed_lines.is_empty());
        assert!(processed_links.is_empty());
        assert!(generated_footnotes.is_empty());
    }

    #[test]
    fn test_convert_links_to_footnotes_max_marker_calculation() {
        // Test lines 116-118 in function `convert_links_to_footnotes`
        let mut interceptor = FootnoteInterceptor::new();

        let existing_footnotes = vec![
            Footnote {
                marker: 5,
                link: "test1".to_string(),
            },
            Footnote {
                marker: 3,
                link: "test2".to_string(),
            },
            Footnote {
                marker: 8,
                link: "test3".to_string(),
            },
        ];

        let (_, _, generated_footnotes) = interceptor.convert_links_to_footnotes(
            vec!["text".to_string()],
            vec![vec![]],
            &existing_footnotes,
        );

        // Should start numbering from max_existing_marker + 1 = 8 + 1 = 9
        // But since there are no links to convert, no footnotes should be generated
        assert!(generated_footnotes.is_empty());
    }

    #[test]
    fn test_convert_links_to_footnotes_unwrap_or_zero() {
        // Test line 120 in function `convert_links_to_footnotes`
        let mut interceptor = FootnoteInterceptor::new();

        // With no existing footnotes, should default to 0
        let (_, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(vec!["text".to_string()], vec![vec![]], &[]);

        assert!(generated_footnotes.is_empty());
    }

    #[test]
    fn test_convert_links_to_footnotes_counter_increment() {
        // Test line 122 in function `convert_links_to_footnotes`
        let mut interceptor = FootnoteInterceptor::new();

        // Should increment from max marker + 1
        let existing_footnotes = vec![Footnote {
            marker: 2,
            link: "existing".to_string(),
        }];

        let (_, _, _) = interceptor.convert_links_to_footnotes(
            vec!["simple text".to_string()],
            vec![vec![]],
            &existing_footnotes,
        );

        // local_footnote_counter should be 2 + 1 = 3, but we can't directly test this
        // since it's only used internally when URI links are found
    }

    #[test]
    fn test_convert_links_to_footnotes_local_counter() {
        // Test line 125 in function `convert_links_to_footnotes`
        let mut interceptor = FootnoteInterceptor::new();

        let existing_footnotes = vec![Footnote {
            marker: 10,
            link: "test".to_string(),
        }];

        let (_, _, _) = interceptor.convert_links_to_footnotes(
            vec!["no links here".to_string()],
            vec![vec![]],
            &existing_footnotes,
        );

        // The local_footnote_counter should be set to 10 + 1 = 11
        // But without actual URI links to process, we can't see this effect
    }

    #[test]
    fn test_multiple_interceptors_in_chain() {
        let mut chain = InterceptorChain::new()
            .add_interceptor(MockInterceptor::new())
            .add_interceptor(MockInterceptor::new());

        chain.initialize();

        let event = Event::BlockFinished;

        let processed_event = chain.process(event);

        // Should pass through both interceptors
        if let Event::BlockFinished = processed_event {
            // Event should remain the same type
        } else {
            panic!("Expected BlockFinished event");
        }

        chain.finalize();
    }

    #[test]
    fn test_interceptor_chain_empty() {
        let mut chain = InterceptorChain::new();

        chain.initialize(); // Should not panic

        let event = Event::BlockFinished;

        let processed_event = chain.process(event);

        // Should pass through unchanged
        if let Event::BlockFinished = processed_event {
            // Event should remain the same
        } else {
            panic!("Expected BlockFinished event");
        }

        chain.finalize(); // Should not panic
    }

    #[test]
    fn test_footnote_interceptor_non_text_event_passthrough() {
        let mut interceptor = FootnoteInterceptor::new();

        let event = Event::BlockStarted {
            meta_line: crate::parsers::wasot::MetaLine {
                type_name: "div".to_string(),
                level: 1,
                id: crate::parsers::wasot::Id::new("test".to_string()),
                arguments: crate::parsers::wasot::ParsedArguments {
                    args: vec![],
                    kwargs: vec![],
                },
                inline_text: crate::parsers::wasot::QuotedString::new("".to_string(), false),
                line_index: Some(0),
                is_void: false,
            },
        };

        let result = interceptor.intercept(event);

        // Should pass through unchanged
        if let Event::BlockStarted { meta_line } = result {
            assert_eq!(meta_line.type_name, "div");
        } else {
            panic!("Expected BlockStarted event");
        }
    }

    #[test]
    fn test_convert_links_zip_iteration() {
        // Test lines 127-128 in unknown context
        use crate::parsers::wasot::parser::TargetType;
        use crate::parsers::wasot::{Link, ParsedArguments, QuotedString};

        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["line1".to_string(), "line2".to_string()];
        let line_links = vec![
            vec![Link {
                name: Some(QuotedString::new("test".to_string(), false)),
                target: None,
                target_type: TargetType::Id,
                arguments: ParsedArguments {
                    args: vec![],
                    kwargs: vec![],
                },
                start_index: 0,
                length: 4,
            }],
            vec![],
        ];

        let (processed_lines, processed_links, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(processed_lines.len(), 2);
        assert_eq!(processed_links.len(), 2);
    }

    #[test]
    fn test_detect_links_empty_spans() {
        // Test lines 130 in unknown context

        let mut interceptor = FootnoteInterceptor::new();

        // Line with no links should be processed unchanged
        let lines = vec!["Plain text with no links".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, processed_links, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Test lines 131-133 in unknown context
        assert_eq!(processed_lines.len(), 1);
        assert_eq!(processed_lines[0], "Plain text with no links");
        assert_eq!(processed_links.len(), 1);
        assert!(processed_links[0].is_empty());
        assert!(generated_footnotes.is_empty());
    }

    #[test]
    fn test_links_processing_initialization() {
        // Test lines 135-138 in unknown context
        use crate::parsers::wasot::detect_links;

        let mut interceptor = FootnoteInterceptor::new();

        let line_with_link = "Visit ((site :https://example.com)) here.";
        let spans = detect_links(line_with_link);

        assert!(!spans.is_empty(), "Should detect at least one link");

        // Test that processing initializes correctly
        let lines = vec![line_with_link.to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, processed_links, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(processed_lines.len(), 1);
        assert_eq!(processed_links.len(), 1);
        assert!(!generated_footnotes.is_empty());
    }

    #[test]
    fn test_link_offset_tracking() {
        // Test line 138 in unknown context - offset tracking

        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec![
            "First ((link1 :https://example.com)) and ((link2 :https://test.com)) end.".to_string(),
        ];
        let line_links = vec![vec![]];

        let (processed_lines, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(processed_lines.len(), 1);
        assert_eq!(generated_footnotes.len(), 2);

        // Should have footnote markers *1 and *2
        assert!(processed_lines[0].contains("*1"));
        assert!(processed_lines[0].contains("*2"));
    }

    #[test]
    fn test_parse_link_and_target_check() {
        // Test lines 140-144 in unknown context
        use crate::parsers::wasot::{detect_links, parse_link};

        let interceptor = FootnoteInterceptor::new();

        let line = "Check ((site :https://example.com)) here.";
        let spans = detect_links(line);
        assert!(!spans.is_empty());

        let link = parse_link(&spans[0].content, spans[0].start_index, spans[0].length);

        // Should have a target
        assert!(link.target.is_some());
        let target = link.target.as_ref().unwrap();

        // Should be a URI link
        assert!(interceptor.is_uri_link(target));
    }

    #[test]
    fn test_uri_link_conversion() {
        // Test lines 145-146 in unknown context - URI link conversion
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Visit ((our site :https://example.com)) now.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _processed_links, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(processed_lines.len(), 1);
        assert!(processed_lines[0].contains("*1"));
        assert_eq!(generated_footnotes.len(), 1);
        assert_eq!(generated_footnotes[0].marker, 1);
        assert_eq!(generated_footnotes[0].link, "https://example.com");
    }

    #[test]
    fn test_new_link_content_construction() {
        // Test line 147 in unknown context - new_link_content initialization
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["See ((docs :https://api.example.com)) here.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should preserve the name "docs" in the processed line
        assert!(processed_lines[0].contains("docs"));
        assert_eq!(generated_footnotes.len(), 1);
    }

    #[test]
    fn test_link_name_preservation() {
        // Test lines 150-151 in unknown context
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Visit ((My Website :https://example.com)) today.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should preserve the link name "My Website"
        assert!(processed_lines[0].contains("My Website"));
        assert!(processed_lines[0].contains("*1"));
    }

    #[test]
    fn test_arguments_processing() {
        // Test lines 155-156 in unknown context - arguments check
        let mut interceptor = FootnoteInterceptor::new();

        let lines =
            vec!["API ((docs .. v=2 format=json :https://api.example.com)) here.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should preserve arguments in the processed line
        assert!(processed_lines[0].contains(" .. "));
        assert!(processed_lines[0].contains("v=2"));
        assert!(processed_lines[0].contains("format=json"));
        assert!(processed_lines[0].contains("*1"));
        assert_eq!(generated_footnotes.len(), 1);
    }

    #[test]
    fn test_argument_dotdot_addition() {
        // Test line 157 in unknown context - .. addition for arguments
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Link ((text .. arg1 :https://example.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should have .. in the processed line
        assert!(processed_lines[0].contains(" .. "));
        assert!(processed_lines[0].contains("arg1"));
    }

    #[test]
    fn test_quoted_argument_processing() {
        // Test lines 158-164 in unknown context - quoted argument processing
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Link ((text .. \"quoted arg\" :https://example.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should preserve quoted arguments
        assert!(processed_lines[0].contains("\"quoted arg\""));
    }

    #[test]
    fn test_unquoted_argument_processing() {
        // Test lines 163-164 in unknown context - unquoted argument
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Link ((text .. unquoted :https://example.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should preserve unquoted arguments without quotes
        assert!(processed_lines[0].contains("unquoted"));
        assert!(!processed_lines[0].contains("\"unquoted\""));
    }

    #[test]
    fn test_kwargs_processing() {
        // Test lines 166-176 in unknown context - kwargs processing
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec![
            "Link ((text .. key1=value1 key2=\"quoted value\" :https://example.com)) end."
                .to_string(),
        ];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should preserve kwargs
        assert!(processed_lines[0].contains("key1=value1"));
        assert!(processed_lines[0].contains("key2=\"quoted value\""));
    }

    #[test]
    fn test_footnote_target_addition() {
        // Test line 180 in unknown context - footnote target formatting
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Link ((site :https://example.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should add :*1 as footnote target
        assert!(processed_lines[0].contains(":*1"));
        assert_eq!(generated_footnotes[0].marker, 1);
    }

    #[test]
    fn test_replacement_string_formatting() {
        // Test line 182 in unknown context - replacement formatting
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Visit ((site :https://example.com)) now.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should be wrapped in (( ))
        assert!(processed_lines[0].contains("((site :*1))"));
    }

    #[test]
    fn test_position_calculation() {
        // Test lines 184-186 in unknown context - position calculation
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Start ((link :https://example.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should properly calculate positions and replace the link
        assert!(processed_lines[0].starts_with("Start "));
        assert!(processed_lines[0].ends_with(" end."));
        assert!(processed_lines[0].contains("*1"));
    }

    #[test]
    fn test_range_replacement() {
        // Test lines 189-191 in unknown context - range replacement
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Text ((link :https://example.com)) more text.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should replace the range correctly
        assert!(processed_lines[0].contains("Text"));
        assert!(processed_lines[0].contains("more text"));
        assert!(processed_lines[0].contains("*1"));
        assert!(!processed_lines[0].contains("https://example.com"));
    }

    #[test]
    fn test_offset_update() {
        // Test line 194 in unknown context - offset update
        let mut interceptor = FootnoteInterceptor::new();

        // Multiple links to test offset tracking
        let lines = vec!["First ((a :https://a.com)) second ((b :https://b.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (processed_lines, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(generated_footnotes.len(), 2);
        assert!(processed_lines[0].contains("*1"));
        assert!(processed_lines[0].contains("*2"));
    }

    #[test]
    fn test_footnote_creation() {
        // Test lines 198-200 in unknown context - footnote creation
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["Link ((site :https://example.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (_, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(generated_footnotes.len(), 1);
        assert_eq!(generated_footnotes[0].marker, 1);
        assert_eq!(generated_footnotes[0].link, "https://example.com");
    }

    #[test]
    fn test_counter_increment() {
        // Test line 203 in unknown context - counter increment
        let mut interceptor = FootnoteInterceptor::new();

        let lines =
            vec!["Link1 ((a :https://a.com)) and link2 ((b :https://b.com)) end.".to_string()];
        let line_links = vec![vec![]];

        let (_, _, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(generated_footnotes.len(), 2);
        assert_eq!(generated_footnotes[0].marker, 1);
        assert_eq!(generated_footnotes[1].marker, 2);
    }

    #[test]
    fn test_non_uri_link_preservation() {
        // Test lines 204-206 in unknown context - non-URI links kept as-is
        use crate::parsers::wasot::parser::TargetType;
        use crate::parsers::wasot::{Link, ParsedArguments, QuotedString};

        let mut interceptor = FootnoteInterceptor::new();

        let non_uri_link = Link {
            name: Some(QuotedString::new("section".to_string(), false)),
            target: Some("section1".to_string()), // Non-URI target
            target_type: TargetType::Id,
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            start_index: 0,
            length: 10,
        };

        let lines = vec!["Text with non-URI link".to_string()];
        let line_links = vec![vec![non_uri_link.clone()]];

        let (_processed_lines, processed_links, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Non-URI links should be preserved
        assert_eq!(processed_links[0].len(), 1);
        assert_eq!(processed_links[0][0].target, Some("section1".to_string()));
        assert!(generated_footnotes.is_empty());
    }

    #[test]
    fn test_link_without_target_preservation() {
        // Test lines 208-210 in unknown context - links without targets
        use crate::parsers::wasot::parser::TargetType;
        use crate::parsers::wasot::{Link, ParsedArguments, QuotedString};

        let mut interceptor = FootnoteInterceptor::new();

        let no_target_link = Link {
            name: Some(QuotedString::new("text".to_string(), false)),
            target: None,
            target_type: TargetType::Id,
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            start_index: 0,
            length: 6,
        };

        let lines = vec!["Text with no-target link".to_string()];
        let line_links = vec![vec![no_target_link.clone()]];

        let (_, processed_links, generated_footnotes) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Links without targets should be preserved
        assert_eq!(processed_links[0].len(), 1);
        assert_eq!(processed_links[0][0].target, None);
        assert!(generated_footnotes.is_empty());
    }

    #[test]
    fn test_processed_lines_push() {
        // Test line 214 in unknown context - processed_lines.push
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["line1".to_string(), "line2".to_string()];
        let line_links = vec![vec![], vec![]];

        let (processed_lines, _, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(processed_lines.len(), 2);
        assert_eq!(processed_lines[0], "line1");
        assert_eq!(processed_lines[1], "line2");
    }

    #[test]
    fn test_processed_links_push() {
        // Test line 215 in unknown context - processed_links.push
        use crate::parsers::wasot::parser::TargetType;
        use crate::parsers::wasot::{Link, ParsedArguments, QuotedString};

        let mut interceptor = FootnoteInterceptor::new();

        let link = Link {
            name: Some(QuotedString::new("test".to_string(), false)),
            target: Some("section1".to_string()),
            target_type: TargetType::Id,
            arguments: ParsedArguments {
                args: vec![],
                kwargs: vec![],
            },
            start_index: 0,
            length: 4,
        };

        let lines = vec!["text".to_string()];
        let line_links = vec![vec![link]];

        let (_, processed_links, _) =
            interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        assert_eq!(processed_links.len(), 1);
        assert_eq!(processed_links[0].len(), 1);
    }

    #[test]
    fn test_return_tuple() {
        // Test line 219 in unknown context - return tuple
        let mut interceptor = FootnoteInterceptor::new();

        let lines = vec!["test".to_string()];
        let line_links = vec![vec![]];

        let result = interceptor.convert_links_to_footnotes(lines, line_links, &[]);

        // Should return a tuple of (Vec<String>, Vec<Vec<Link>>, Vec<Footnote>)
        assert_eq!(result.0.len(), 1);
        assert_eq!(result.1.len(), 1);
        assert_eq!(result.2.len(), 0);
    }

    #[test]
    fn test_footnote_interceptor_default_new() {
        // Test lines 224-225 in function `default`
        let interceptor1 = FootnoteInterceptor::default();
        let interceptor2 = FootnoteInterceptor::new();

        assert_eq!(
            interceptor1.process_uri_links,
            interceptor2.process_uri_links
        );
    }

    #[test]
    fn test_intercept_textfinished_event() {
        // Test lines 230-232 in function `intercept`
        let mut interceptor = FootnoteInterceptor::new();

        let event = Event::TextFinished {
            lines: vec!["Simple text".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        };

        let result = interceptor.intercept(event);

        if let Event::TextFinished {
            lines,
            links,
            footnotes,
        } = result
        {
            assert_eq!(lines.len(), 1);
            assert_eq!(links.len(), 1);
            assert_eq!(footnotes.len(), 0);
        } else {
            panic!("Expected TextFinished event");
        }
    }

    #[test]
    fn test_intercept_convert_links_call() {
        // Test line 238 in function `intercept` - convert_links_to_footnotes call
        let mut interceptor = FootnoteInterceptor::new();

        let event = Event::TextFinished {
            lines: vec!["Link ((site :https://example.com)) here".to_string()],
            links: vec![vec![]],
            footnotes: vec![],
        };

        let result = interceptor.intercept(event);

        if let Event::TextFinished {
            lines, footnotes, ..
        } = result
        {
            assert!(lines[0].contains("*1"));
            assert_eq!(footnotes.len(), 1);
        } else {
            panic!("Expected TextFinished event");
        }
    }

    #[test]
    fn test_footnotes_append() {
        // Test line 242 in function `intercept` - footnotes.append
        let mut interceptor = FootnoteInterceptor::new();

        let existing_footnote = Footnote {
            marker: 5,
            link: "existing".to_string(),
        };

        let event = Event::TextFinished {
            lines: vec!["Link ((site :https://example.com)) here".to_string()],
            links: vec![vec![]],
            footnotes: vec![existing_footnote],
        };

        let result = interceptor.intercept(event);

        if let Event::TextFinished { footnotes, .. } = result {
            // Should have existing + generated footnotes
            assert_eq!(footnotes.len(), 2);
            assert_eq!(footnotes[0].marker, 5); // existing
            assert_eq!(footnotes[1].marker, 6); // generated (5+1)
        } else {
            panic!("Expected TextFinished event");
        }
    }

    #[test]
    fn test_intercept_other_event_passthrough() {
        // Test line 250 in function `intercept` - other event passthrough
        let mut interceptor = FootnoteInterceptor::new();

        let event = Event::BlockFinished;
        let result = interceptor.intercept(event);

        if let Event::BlockFinished = result {
            // Should pass through unchanged
        } else {
            panic!("Expected BlockFinished event");
        }
    }
}
