#[cfg(test)]
#[allow(clippy::module_inception)]
mod fuzz_tests {
    use super::super::parser::{extract_line_type, is_meta_line, split_into_parts, MetaLine};
    use rand::{seq::SliceRandom, thread_rng, Rng};

    // Generate random strings with various edge cases
    fn generate_random_string(rng: &mut impl Rng, max_len: usize) -> String {
        let charset: Vec<char> = vec![
            // ASCII
            'a', 'b', 'c', 'A', 'B', 'C', '0', '1', '2', ' ', '\t', '\n', '\r', '.', '"', '\\', ':',
            '=', '-', '_', '@', '#', '$', '%', '^', '&', '*', '(', ')', '[', ']', '{', '}', '<',
            '>', '/', '|', '`', '~', '!', '?', // Unicode
            '😀', '🦡', '∑', '∏', '∫', '日', '本', '語', '한', '글', 'τ', 'ύ', 'π', 'ο', 'ς',
            '\u{200B}', '\u{200C}', '\u{200D}',
            '\u{00A0}', // Zero-width and non-breaking spaces
            // Control characters
            '\0', '\x01', '\x02', '\x03', '\x07', '\x08', '\x0B',
        ];

        let len = rng.gen_range(0..=max_len);
        (0..len).map(|_| *charset.choose(rng).unwrap()).collect()
    }

    // Generate a random meta line
    fn generate_random_meta_line(rng: &mut impl Rng) -> String {
        let dots = ".".repeat(rng.gen_range(1..=10));
        let type_name = if rng.gen_bool(0.3) {
            // Sometimes use quoted type
            format!("\"{}\"", generate_random_string(rng, 20))
        } else {
            generate_random_string(rng, 20)
        };

        let mut line = format!("{dots}{type_name}");

        // Sometimes add inline text
        if rng.gen_bool(0.5) {
            line.push(' ');
            line.push_str(&generate_random_string(rng, 50));
        }

        // Sometimes add parameters
        if rng.gen_bool(0.5) {
            line.push_str(" .. ");
            for _ in 0..rng.gen_range(0..5) {
                if rng.gen_bool(0.5) {
                    // Positional arg
                    line.push_str(&generate_random_string(rng, 10));
                } else {
                    // Keyword arg
                    line.push_str(&format!(
                        "{}={}",
                        generate_random_string(rng, 10),
                        generate_random_string(rng, 10)
                    ));
                }
                line.push(' ');
            }
        }

        // Sometimes add ID
        if rng.gen_bool(0.3) {
            line.push_str(&format!(" :{}", generate_random_string(rng, 20)));
        }

        line
    }

    #[test]
    #[ignore = "slow"] // Slow test: takes 60+ seconds. Run with --ignored to include.
    fn fuzz_meta_line_parser() {
        let mut rng = thread_rng();

        for _ in 0..10000 {
            let input = generate_random_meta_line(&mut rng);

            // The parser should never panic
            if is_meta_line(&input) {
                let _ = MetaLine::parse(&input, 0);
            }
        }
    }

    #[test]
    #[ignore = "slow"] // Slow test: takes 3+ seconds. Run with --ignored to include.
    fn fuzz_split_into_parts() {
        let mut rng = thread_rng();

        for _ in 0..10000 {
            let input = generate_random_string(&mut rng, 1000);

            // split_into_parts should never panic
            let parts = split_into_parts(&input);

            // Basic property: joining with ".." should give back something related to original
            // (not exactly the same due to how splitting works, but shouldn't panic)
            let _rejoined = parts.join("..");
        }
    }

    #[test]
    #[ignore = "slow"] // Potentially slow test: 10k iterations. Run with --ignored to include.
    fn fuzz_extract_line_type() {
        let mut rng = thread_rng();

        for _ in 0..10000 {
            let input = format!(".{}", generate_random_string(&mut rng, 100));

            // extract_line_type should never panic
            let (type_name, remaining, _is_void) = extract_line_type(&input);

            // Basic property checks
            assert!(type_name.len() + remaining.len() <= input.len() + 10); // Some slack for processing
        }
    }

    #[test]
    #[ignore = "slow"] // Slow test: takes 225ms. Run with --ignored to include.
    fn fuzz_empty_and_special_inputs() {
        // Test specifically problematic inputs
        let dots_10k = ".".repeat(10000);
        let quotes_10k = "\"".repeat(10000);
        let backslash_10k = "\\".repeat(10000);
        let nulls_10k = "\0".repeat(10000);
        let badgers_1k = ".🦡".repeat(1000);

        let special_inputs = vec![
            "",
            ".",
            "..",
            "...",
            ".\0",
            ".\0\0\0",
            ".\"",
            ".\"\"",
            ".\"\\\"",
            ".\\",
            ".\\\\",
            dots_10k.as_str(),
            quotes_10k.as_str(),
            backslash_10k.as_str(),
            nulls_10k.as_str(),
            ".\u{200B}\u{200C}\u{200D}",
            badgers_1k.as_str(),
        ];

        for input in special_inputs {
            // None of these should panic
            let _ = is_meta_line(input);
            if is_meta_line(input) {
                let _ = MetaLine::parse(input, 0);
            }
            let _ = split_into_parts(input);
            if input.starts_with('.') {
                let _ = extract_line_type(input);
            }
        }
    }

    #[test]
    #[ignore = "slow"] // Slow test: takes 249ms. Run with --ignored to include.
    fn stress_test_parser_memory() {
        // Test with extremely large inputs to ensure no memory issues
        let huge_type = "a".repeat(1_000_000);
        let huge_line = format!(".{huge_type}");

        if let Some(meta) = MetaLine::parse(&huge_line, 0) {
            assert_eq!(meta.type_name.len(), 1_000_000);
        }

        // Test with many dots
        let many_dots = ".".repeat(100_000) + "type";
        if let Some(meta) = MetaLine::parse(&many_dots, 0) {
            assert_eq!(meta.level, 100_000);
        }
    }

    #[test]
    #[ignore = "slow"] // Slow test: takes 6+ seconds. Run with --ignored to include.
    fn property_based_tests() {
        let mut rng = thread_rng();

        for _ in 0..1000 {
            let line = generate_random_meta_line(&mut rng);

            if let Some(meta) = MetaLine::parse(&line, 42) {
                // Properties that should always hold
                assert!(meta.level > 0, "Level should be at least 1");
                assert_eq!(meta.line_index, Some(42), "Line index should be preserved");

                // IDs can now contain uppercase, Unicode, and digits
                if !meta.id.is_empty() {
                    // Just verify it's not empty and doesn't contain whitespace
                    assert!(
                        !meta.id.as_str().chars().any(|c| c.is_whitespace()),
                        "ID should not contain whitespace"
                    );
                }
            }
        }
    }
}
