#[derive(Debug, Clone, PartialEq)]
pub struct Footnote {
    pub marker: u32,
    pub link: String,
}

/// Parses footnotes from a list of text lines.
/// Footnotes are lines that start with * followed by one or more digits,
/// then whitespace, then the link content.
/// For example: "*1 https://example.com"
#[allow(dead_code)]
pub fn parse_footnotes(lines: &[String]) -> Vec<Footnote> {
    let mut footnotes = Vec::new();

    for line in lines {
        if let Some(footnote) = parse_footnote_line(line) {
            footnotes.push(footnote);
        }
    }

    footnotes
}

/// Parses a single line to extract a footnote if it matches the pattern.
/// Returns None if the line doesn't match the footnote pattern.
#[allow(dead_code)]
fn parse_footnote_line(line: &str) -> Option<Footnote> {
    let trimmed = line.trim();

    // Must start with *
    if !trimmed.starts_with('*') {
        return None;
    }

    // Find where the digits end
    let mut digit_end = 1; // Start after the *
    let chars: Vec<char> = trimmed.chars().collect();

    // Must have at least one digit after *
    if chars.len() <= 1 || !chars[1].is_ascii_digit() {
        return None;
    }

    // Find end of digit sequence
    while digit_end < chars.len() && chars[digit_end].is_ascii_digit() {
        digit_end += 1;
    }

    // Parse the marker number
    let marker_str = &trimmed[1..digit_end];
    let marker = match marker_str.parse::<u32>() {
        Ok(num) => num,
        Err(_) => return None,
    };

    // Must have whitespace after the digits
    if digit_end >= chars.len() || !chars[digit_end].is_whitespace() {
        return None;
    }

    // Extract the link (everything after the whitespace)
    let link = trimmed[digit_end..].trim().to_string();

    // Link cannot be empty
    if link.is_empty() {
        return None;
    }

    Some(Footnote { marker, link })
}

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

    #[test]
    fn test_parse_simple_footnote() {
        let footnote = parse_footnote_line("*1 https://example.com").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_with_multiple_digits() {
        let footnote = parse_footnote_line("*123 https://example.com").unwrap();
        assert_eq!(footnote.marker, 123);
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_with_zero() {
        let footnote = parse_footnote_line("*0 https://example.com").unwrap();
        assert_eq!(footnote.marker, 0);
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_with_whitespace_padding() {
        let footnote = parse_footnote_line("  *1 https://example.com  ").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_with_multiple_spaces() {
        let footnote = parse_footnote_line("*1    https://example.com").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_with_tab() {
        let footnote = parse_footnote_line("*1\thttps://example.com").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_not_starting_with_star() {
        assert!(parse_footnote_line("1 https://example.com").is_none());
    }

    #[test]
    fn test_parse_footnote_star_without_digits() {
        assert!(parse_footnote_line("* https://example.com").is_none());
    }

    #[test]
    fn test_parse_footnote_star_with_letters() {
        assert!(parse_footnote_line("*abc https://example.com").is_none());
    }

    #[test]
    fn test_parse_footnote_no_whitespace_after_digits() {
        assert!(parse_footnote_line("*1https://example.com").is_none());
    }

    #[test]
    fn test_parse_footnote_empty_link() {
        assert!(parse_footnote_line("*1 ").is_none());
        assert!(parse_footnote_line("*1   ").is_none());
        assert!(parse_footnote_line("*1\t").is_none());
    }

    #[test]
    fn test_parse_footnote_with_mixed_digits_and_letters() {
        assert!(parse_footnote_line("*1a https://example.com").is_none());
        assert!(parse_footnote_line("*a1 https://example.com").is_none());
    }

    #[test]
    fn test_parse_footnote_large_marker() {
        let footnote = parse_footnote_line("*999999 https://example.com").unwrap();
        assert_eq!(footnote.marker, 999999);
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_with_complex_url() {
        let footnote =
            parse_footnote_line("*1 https://example.com/path?param=value&other=123#section")
                .unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(
            footnote.link,
            "https://example.com/path?param=value&other=123#section"
        );
    }

    #[test]
    fn test_parse_footnote_with_non_url_link() {
        let footnote = parse_footnote_line("*1 See chapter 5 for more details").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "See chapter 5 for more details");
    }

    #[test]
    fn test_parse_footnote_only_star() {
        assert!(parse_footnote_line("*").is_none());
    }

    #[test]
    fn test_parse_footnote_empty_line() {
        assert!(parse_footnote_line("").is_none());
    }

    #[test]
    fn test_parse_footnote_only_whitespace() {
        assert!(parse_footnote_line("   ").is_none());
    }

    #[test]
    fn test_parse_footnote_star_at_end() {
        assert!(parse_footnote_line("some text *").is_none());
    }

    #[test]
    fn test_parse_footnote_star_in_middle() {
        assert!(parse_footnote_line("some *1 text").is_none());
    }

    #[test]
    fn test_parse_footnote_multiple_stars() {
        assert!(parse_footnote_line("**1 https://example.com").is_none());
    }

    #[test]
    fn test_parse_footnote_with_newline_in_link() {
        // Newlines in links should be preserved as-is
        let footnote = parse_footnote_line("*1 Link with\nnewline").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "Link with\nnewline");
    }

    #[test]
    fn test_parse_footnote_with_unicode_marker() {
        // Non-ASCII digits should not be accepted
        assert!(parse_footnote_line("*１ https://example.com").is_none()); // Full-width digit
    }

    #[test]
    fn test_parse_footnote_with_special_characters_in_link() {
        let footnote = parse_footnote_line("*1 Link with @#$%^&*()[]{}|;'<>?,./").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "Link with @#$%^&*()[]{}|;'<>?,./");
    }

    #[test]
    fn test_parse_footnotes_multiple_lines() {
        let lines = vec![
            "*1 https://example.com".to_string(),
            "not a footnote".to_string(),
            "*2 https://another.com".to_string(),
            "*invalid https://bad.com".to_string(),
            "*3 https://third.com".to_string(),
        ];

        let footnotes = parse_footnotes(&lines);
        assert_eq!(footnotes.len(), 3);

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

        assert_eq!(footnotes[1].marker, 2);
        assert_eq!(footnotes[1].link, "https://another.com");

        assert_eq!(footnotes[2].marker, 3);
        assert_eq!(footnotes[2].link, "https://third.com");
    }

    #[test]
    fn test_parse_footnotes_empty_list() {
        let lines: Vec<String> = vec![];
        let footnotes = parse_footnotes(&lines);
        assert_eq!(footnotes.len(), 0);
    }

    #[test]
    fn test_parse_footnotes_no_valid_footnotes() {
        let lines = vec![
            "just text".to_string(),
            "more text".to_string(),
            "* invalid".to_string(),
            "*abc invalid".to_string(),
        ];

        let footnotes = parse_footnotes(&lines);
        assert_eq!(footnotes.len(), 0);
    }

    #[test]
    fn test_parse_footnote_leading_zeros() {
        let footnote = parse_footnote_line("*001 https://example.com").unwrap();
        assert_eq!(footnote.marker, 1); // Leading zeros should be ignored
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_max_u32() {
        let footnote = parse_footnote_line("*4294967295 https://example.com").unwrap();
        assert_eq!(footnote.marker, 4294967295); // Max u32 value
        assert_eq!(footnote.link, "https://example.com");
    }

    #[test]
    fn test_parse_footnote_overflow_u32() {
        // This should fail because the number is too large for u32
        assert!(parse_footnote_line("*4294967296 https://example.com").is_none());
    }

    #[test]
    fn test_parse_footnote_very_long_link() {
        let long_link = "https://".to_string() + &"a".repeat(1000) + ".com";
        let line = format!("*1 {long_link}");
        let footnote = parse_footnote_line(&line).unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, long_link);
    }

    #[test]
    fn test_parse_footnote_unicode_in_link() {
        let footnote = parse_footnote_line("*1 https://example.com/path/世界").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "https://example.com/path/世界");
    }

    #[test]
    fn test_parse_footnote_emoji_in_link() {
        let footnote = parse_footnote_line("*1 Check out this 🎉 amazing site").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "Check out this 🎉 amazing site");
    }

    #[test]
    fn test_parse_footnote_with_control_characters() {
        let footnote = parse_footnote_line("*1 Link\x00with\x01control\x02chars").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "Link\x00with\x01control\x02chars");
    }

    #[test]
    fn test_parse_footnote_mixed_whitespace() {
        let footnote = parse_footnote_line("*1 \t  Link with mixed whitespace  \t ").unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "Link with mixed whitespace");
    }

    #[test]
    fn test_parse_footnote_duplicate_markers() {
        let lines = vec![
            "*1 https://first.com".to_string(),
            "*1 https://second.com".to_string(), // Duplicate marker
            "*2 https://third.com".to_string(),
        ];

        let footnotes = parse_footnotes(&lines);
        assert_eq!(footnotes.len(), 3);

        // Both footnotes with marker 1 should be preserved
        assert_eq!(footnotes[0].marker, 1);
        assert_eq!(footnotes[0].link, "https://first.com");
        assert_eq!(footnotes[1].marker, 1);
        assert_eq!(footnotes[1].link, "https://second.com");
        assert_eq!(footnotes[2].marker, 2);
        assert_eq!(footnotes[2].link, "https://third.com");
    }

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

    #[test]
    fn test_parse_footnotes_vec_new() {
        // Test lines 12-13: let mut footnotes = Vec::new();
        let lines = vec!["*1 test".to_string()];
        let footnotes = parse_footnotes(&lines);
        // Vec::new() is called and footnote is added
        assert_eq!(footnotes.len(), 1);
    }

    #[test]
    fn test_parse_footnotes_for_loop() {
        // Test lines 15-17: for line in lines iteration
        let lines = vec![
            "*1 first".to_string(),
            "not a footnote".to_string(),
            "*2 second".to_string(),
        ];
        let footnotes = parse_footnotes(&lines);

        // Should process all lines in the loop
        assert_eq!(footnotes.len(), 2);
        assert_eq!(footnotes[0].marker, 1);
        assert_eq!(footnotes[1].marker, 2);
    }

    #[test]
    fn test_parse_footnotes_return_vec() {
        // Test line 21: footnotes return
        let lines = vec!["*5 return test".to_string()];
        let result = parse_footnotes(&lines);

        // Function should return the footnotes vector
        assert_eq!(result.len(), 1);
        assert_eq!(result[0].marker, 5);
        assert_eq!(result[0].link, "return test");
    }

    #[test]
    fn test_parse_footnote_line_trim() {
        // Test lines 27-28: let trimmed = line.trim();
        let footnote = parse_footnote_line("  *42 trimmed  ");
        assert!(footnote.is_some());
        assert_eq!(footnote.unwrap().marker, 42);
    }

    #[test]
    fn test_parse_footnote_line_starts_with_star() {
        // Test lines 31-32: if !trimmed.starts_with('*') return None
        let result = parse_footnote_line("not starting with star");
        assert!(result.is_none());

        let result2 = parse_footnote_line("  also not starting");
        assert!(result2.is_none());
    }

    #[test]
    fn test_parse_footnote_line_digit_end_initialization() {
        // Test lines 36-37: let mut digit_end = 1; let chars: Vec<char> = ...
        let footnote = parse_footnote_line("*999 test");
        assert!(footnote.is_some());
        assert_eq!(footnote.unwrap().marker, 999);
    }

    #[test]
    fn test_parse_footnote_line_length_and_digit_check() {
        // Test lines 40-41: if chars.len() <= 1 || !chars[1].is_ascii_digit()
        let result1 = parse_footnote_line("*");
        assert!(result1.is_none()); // chars.len() <= 1

        let result2 = parse_footnote_line("*a");
        assert!(result2.is_none()); // !chars[1].is_ascii_digit()

        let result3 = parse_footnote_line("*5 valid");
        assert!(result3.is_some()); // Both conditions false, should succeed
    }

    #[test]
    fn test_parse_footnote_line_digit_loop() {
        // Test lines 45-46: while digit_end < chars.len() && chars[digit_end].is_ascii_digit()
        let footnote = parse_footnote_line("*12345 test");
        assert!(footnote.is_some());
        assert_eq!(footnote.unwrap().marker, 12345);

        let footnote2 = parse_footnote_line("*7a test"); // Should stop at 'a'
        assert!(footnote2.is_none()); // No whitespace after digits
    }

    #[test]
    fn test_parse_footnote_line_marker_parsing() {
        // Test lines 50-53: let marker_str = ...; let marker = match marker_str.parse::<u32>()
        let footnote = parse_footnote_line("*123 test");
        assert!(footnote.is_some());
        assert_eq!(footnote.unwrap().marker, 123);

        // Test parse error case (though practically unreachable with our validation)
        // We've already validated digits, so this mainly tests the structure
    }

    #[test]
    fn test_parse_footnote_line_whitespace_check() {
        // Test lines 57-58: if digit_end >= chars.len() || !chars[digit_end].is_whitespace()
        let result1 = parse_footnote_line("*1");
        assert!(result1.is_none()); // digit_end >= chars.len()

        let result2 = parse_footnote_line("*1x");
        assert!(result2.is_none()); // !chars[digit_end].is_whitespace()

        let result3 = parse_footnote_line("*1 valid");
        assert!(result3.is_some()); // Has whitespace
    }

    #[test]
    fn test_parse_footnote_line_link_extraction() {
        // Test line 62: let link = trimmed[digit_end..].trim().to_string();
        let footnote = parse_footnote_line("*1   extracted link   ");
        assert!(footnote.is_some());
        assert_eq!(footnote.unwrap().link, "extracted link");
    }

    #[test]
    fn test_parse_footnote_line_empty_link_check() {
        // Test lines 65-66: if link.is_empty() return None
        let result1 = parse_footnote_line("*1 ");
        assert!(result1.is_none());

        let result2 = parse_footnote_line("*1   ");
        assert!(result2.is_none());

        let result3 = parse_footnote_line("*1 not_empty");
        assert!(result3.is_some());
    }

    #[test]
    fn test_parse_footnote_line_successful_return() {
        // Test line 69: Some(Footnote { marker, link })
        let result = parse_footnote_line("*42 successful");
        assert!(result.is_some());

        let footnote = result.unwrap();
        assert_eq!(footnote.marker, 42);
        assert_eq!(footnote.link, "successful");
    }

    // Additional edge case tests for complete coverage

    #[test]
    fn test_parse_footnote_line_maximum_line_length() {
        // Test with extremely long line to ensure no buffer overflows
        let very_long_content = "a".repeat(100_000);
        let line = format!("*1 {very_long_content}");
        let footnote = parse_footnote_line(&line).unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link.len(), 100_000);
        assert!(footnote.link.chars().all(|c| c == 'a'));
    }

    #[test]
    fn test_parse_footnote_line_unusual_whitespace_combinations() {
        // Test with various Unicode whitespace characters
        let footnote1 = parse_footnote_line("*1\u{00A0}non-breaking space").unwrap(); // Non-breaking space
        assert_eq!(footnote1.marker, 1);
        assert_eq!(footnote1.link, "non-breaking space");

        let footnote2 = parse_footnote_line("*2\u{2000}en quad").unwrap(); // En quad
        assert_eq!(footnote2.marker, 2);
        assert_eq!(footnote2.link, "en quad");

        let footnote3 = parse_footnote_line("*3\u{3000}ideographic space").unwrap(); // Ideographic space
        assert_eq!(footnote3.marker, 3);
        assert_eq!(footnote3.link, "ideographic space");
    }

    #[test]
    fn test_parse_footnote_line_mixed_unusual_whitespace() {
        // Test with combination of different whitespace types
        let footnote = parse_footnote_line("*42\t \u{00A0}\u{2000} mixed whitespace test").unwrap();
        assert_eq!(footnote.marker, 42);
        assert_eq!(footnote.link, "mixed whitespace test");
    }

    #[test]
    fn test_parse_footnotes_very_large_input_array() {
        // Test with large number of lines to ensure performance and memory handling
        let mut lines = Vec::new();
        for i in 1..=10_000 {
            lines.push(format!("*{i} https://example{i}.com"));
        }

        let footnotes = parse_footnotes(&lines);
        assert_eq!(footnotes.len(), 10_000);

        // Check first and last entries
        assert_eq!(footnotes[0].marker, 1);
        assert_eq!(footnotes[0].link, "https://example1.com");
        assert_eq!(footnotes[9999].marker, 10_000);
        assert_eq!(footnotes[9999].link, "https://example10000.com");
    }

    #[test]
    fn test_parse_footnotes_mixed_large_input() {
        // Test with large input containing mix of valid and invalid footnotes
        let mut lines = Vec::new();
        for i in 1..=5_000 {
            if i % 3 == 0 {
                lines.push(format!("*{i} https://valid{i}.com"));
            } else if i % 3 == 1 {
                lines.push(format!("invalid line {i}"));
            } else {
                lines.push(format!("*invalid{i} bad format"));
            }
        }

        let footnotes = parse_footnotes(&lines);
        // Should only have footnotes where i % 3 == 0
        assert_eq!(footnotes.len(), 5_000 / 3); // 1666 valid footnotes
    }

    #[test]
    fn test_parse_footnote_line_potential_panic_conditions() {
        // Test edge cases that could potentially cause panics

        // Single character strings
        assert!(parse_footnote_line("a").is_none());
        assert!(parse_footnote_line("*").is_none());
        assert!(parse_footnote_line("1").is_none());

        // Empty string after trim
        assert!(parse_footnote_line("   ").is_none());
        assert!(parse_footnote_line("\t\n").is_none());

        // Boundary conditions with indexing
        let result = parse_footnote_line("*0");
        assert!(result.is_none()); // No whitespace, should fail

        let result = parse_footnote_line("*0 ");
        assert!(result.is_none()); // Empty link after trim

        // Test string slicing boundaries
        let result = parse_footnote_line("*999999999999999999999 test"); // Very long number
                                                                         // Should handle parsing failure gracefully
        assert!(result.is_none()); // Should fail due to u32 overflow
    }

    #[test]
    fn test_parse_footnote_line_char_collection_bounds() {
        // Test character collection and indexing edge cases
        let test_cases = [
            "*",    // Length 1
            "*a",   // Length 2, invalid digit
            "*1",   // Length 2, no whitespace
            "*12",  // Length 3, no whitespace
            "*1 ",  // Length 3, empty link
            "*1  ", // Length 4, empty link with extra space
        ];

        for case in &test_cases {
            let result = parse_footnote_line(case);
            assert!(result.is_none(), "Case '{case}' should be None");
        }
    }

    #[test]
    fn test_parse_footnote_line_string_slicing_safety() {
        // Test that string slicing is safe with various inputs
        let footnote = parse_footnote_line("*123456789 test").unwrap();
        assert_eq!(footnote.marker, 123456789);
        assert_eq!(footnote.link, "test");

        // Test with exactly boundary conditions
        let footnote = parse_footnote_line("*1 x").unwrap(); // Minimal valid case
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link, "x");
    }

    #[test]
    fn test_parse_footnotes_empty_and_null_conditions() {
        // Test various empty conditions
        assert_eq!(parse_footnotes(&[]), Vec::new());
        assert_eq!(parse_footnotes(&[String::new()]), Vec::new());
        assert_eq!(parse_footnotes(&["".to_string()]), Vec::new());
        assert_eq!(
            parse_footnotes(&[String::new(), "".to_string(), "   ".to_string()]),
            Vec::new()
        );
    }

    #[test]
    fn test_parse_footnote_line_memory_safety() {
        // Test with strings that could cause memory issues
        let long_marker = "*".to_string() + &"9".repeat(50); // Very long number
        let result = parse_footnote_line(&(long_marker + " test"));
        assert!(result.is_none()); // Should fail parsing as u32

        // Test with very long link
        let long_link = "a".repeat(1_000_000); // 1MB string
        let line = format!("*1 {long_link}");
        let footnote = parse_footnote_line(&line).unwrap();
        assert_eq!(footnote.marker, 1);
        assert_eq!(footnote.link.len(), 1_000_000);
    }
}
