//! Comprehensive edge case tests for the selector system
//!
//! This module tests nasty edge cases that might break the selector parser
//! or matching logic, including malformed input, extreme values, and
//! unusual but valid CSS patterns.

#[cfg(test)]
mod tests {
    use crate::parsers::selectors::{ast::*, matches_full, parse_selector};
    use crate::MetaLine;

    // ==============================================================================
    // Parser Edge Cases - Malformed Input
    // ==============================================================================

    #[test]
    fn test_unclosed_quotes() {
        let malformed = vec![
            r#"."unclosed quote"#,
            r#"#"unclosed id"#,
            r#"["unclosed attr"#,
            r#"[attr="unclosed value"#,
            r#"."class with \"escaped but unclosed"#,
        ];

        for selector_str in malformed {
            let result = parse_selector(selector_str);
            assert!(
                result.is_err(),
                "Should fail on unclosed quote: {selector_str}"
            );
        }
    }

    #[test]
    fn test_invalid_escape_sequences() {
        let test_cases = vec![
            r#".\invalid"#,          // Backslash in class
            r#".class\\"#,           // Trailing backslash
            r#"[attr="\x"]"#,        // Invalid hex escape
            r#"[attr="trailing\"]"#, // Trailing backslash in value
            r#"#id\0null"#,          // Null character
        ];

        for selector_str in test_cases {
            let _result = parse_selector(selector_str);
            // Some might parse, some might fail - we're testing for panics
        }
    }

    #[test]
    fn test_extreme_nesting_depth() {
        // Build a selector with extreme nesting
        let mut selector = String::new();
        for i in 0..1000 {
            if i > 0 {
                selector.push_str(" > ");
            }
            selector.push_str(&format!("div:not(.class{i})"));
        }

        let result = parse_selector(&selector);
        // Should either parse or fail gracefully, not panic
        match result {
            Ok(_) => println!("Extreme nesting parsed successfully"),
            Err(e) => println!("Extreme nesting failed gracefully: {e}"),
        }
    }

    #[test]
    fn test_recursive_not_selectors() {
        // CSS doesn't allow :not() inside :not(), but let's test it
        let nested_not = vec![
            "div:not(:not(p))",
            "div:not(:not(:not(span)))",
            ":not(:not(:not(:not(:not(a)))))",
        ];

        for selector_str in nested_not {
            let result = parse_selector(selector_str);
            // Should fail or handle gracefully
            match result {
                Ok(_) => println!("Nested :not() handled: {selector_str}"),
                Err(e) => println!("Nested :not() rejected: {selector_str} - {e}"),
            }
        }
    }

    #[test]
    fn test_empty_and_whitespace_edge_cases() {
        let edge_cases = vec![
            "",                      // Empty
            "   ",                   // Only spaces
            "\t\n\r",                // Only whitespace
            "   div   ",             // Trimming test
            "div\u{00A0}p",          // Non-breaking space as separator
            "div\u{2003}>\u{2003}p", // Em space around combinator
        ];

        for selector_str in edge_cases {
            let result = parse_selector(selector_str);
            match result {
                Ok(_group) => {
                    if selector_str.trim().is_empty() {
                        panic!("Empty selector should not parse: '{selector_str}'");
                    }
                }
                Err(_) => {
                    if !selector_str.trim().is_empty()
                        && selector_str.contains(|c: char| c.is_alphabetic())
                    {
                        println!("Valid selector failed: '{selector_str}'");
                    }
                }
            }
        }
    }

    #[test]
    fn test_combinators_with_no_operands() {
        let invalid = vec![
            "> div",
            "+ div",
            "~ div",
            "div > > p",
            "div + + p",
            "div ~ ~ p",
            "> > >",
            "+ ~ >",
        ];

        for selector_str in invalid {
            let result = parse_selector(selector_str);
            assert!(
                result.is_err(),
                "Invalid combinator usage should fail: {selector_str}"
            );
        }
    }

    #[test]
    fn test_pseudo_class_edge_cases() {
        let edge_cases = vec![
            ":nth-child(-1)",                // Negative index
            ":nth-child(0)",                 // Zero index (invalid in CSS)
            ":nth-child(999999999999)",      // Very large number
            ":nth-child(2n)",                // Formula (not supported yet)
            ":nth-child(odd)",               // Keyword (not supported yet)
            ":nth-child(1.5)",               // Decimal
            ":nth-child(1e10)",              // Scientific notation
            ":unknown-pseudo",               // Unknown pseudo-class
            "::before",                      // Pseudo-element (double colon)
            ":not(:first-child:last-child)", // Multiple pseudo in :not()
        ];

        for selector_str in edge_cases {
            let result = parse_selector(selector_str);
            match result {
                Ok(_) => println!("Pseudo-class parsed: {selector_str}"),
                Err(e) => println!("Pseudo-class rejected: {selector_str} - {e}"),
            }
        }
    }

    #[test]
    fn test_attribute_selector_edge_cases() {
        let edge_cases = vec![
            "[attr=]",                   // Empty value
            "[=value]",                  // Empty key
            "[attr = value]",            // Spaces around =
            r#"[attr=""]"#,              // Empty quoted value
            r#"[attr='single quotes']"#, // Single quotes
            r#"[attr=unquoted]"#,        // Unquoted value
            r#"[attr="a\"b\"c"]"#,       // Multiple escaped quotes
            r#"[attr*=]"#,               // Empty substring match
            r#"[attr~=]"#,               // Empty token match
            "[attr==value]",             // Double equals
            "[attr=value=more]",         // Multiple equals
            r#"[attr="[brackets]"]"#,    // Brackets in value
            r#"[attr="{braces}"]"#,      // Braces in value
            "[attr=\"\"]",               // Complex escaping
            "[attr='can\\'t']",          // Apostrophe escaping
        ];

        for selector_str in edge_cases {
            let result = parse_selector(selector_str);
            match result {
                Ok(_) => println!("Attribute selector parsed: {selector_str}"),
                Err(e) => println!("Attribute selector rejected: {selector_str} - {e}"),
            }
        }
    }

    #[test]
    fn test_class_and_id_edge_cases() {
        let edge_cases = vec![
            ".123class",            // Starting with number
            "#123id",               // ID starting with number
            ".-dash-class",         // Starting with dash
            "#-dash-id",            // ID starting with dash
            ".class.class",         // Duplicate class
            "#id#id2",              // Multiple IDs (invalid)
            ".class..class2",       // Double dots
            "..double-dot",         // Starting with double dot
            ".class\\.with\\.dots", // Escaped dots
            "#id\\#with\\#hash",    // Escaped hashes
            ".класс",               // Cyrillic
            "#아이디",              // Korean
            ".😀",                  // Emoji (might not be valid CSS)
            "#\u{200B}invisible",   // Zero-width space
        ];

        for selector_str in edge_cases {
            let result = parse_selector(selector_str);
            match result {
                Ok(_) => println!("Class/ID parsed: {selector_str}"),
                Err(e) => println!("Class/ID rejected: {selector_str} - {e}"),
            }
        }
    }

    #[test]
    fn test_universal_selector_combinations() {
        let test_cases = vec![
            "*",
            "* > div",
            "div > *",
            "* + *",
            "*:first-child",
            "*#id",    // Universal with ID (redundant but valid)
            "*.class", // Universal with class
            "*[attr]", // Universal with attribute
            "**",      // Double universal (invalid)
            "div * p", // Universal in chain
        ];

        for selector_str in test_cases {
            let result = parse_selector(selector_str);
            match result {
                Ok(_) => println!("Universal selector parsed: {selector_str}"),
                Err(e) => println!("Universal selector rejected: {selector_str} - {e}"),
            }
        }
    }

    // ==============================================================================
    // Matching Edge Cases
    // ==============================================================================

    #[test]
    fn test_matching_with_empty_attributes() {
        let selector = parse_selector(r#"[data-empty=""]"#).unwrap();

        // Element with empty attribute value
        let element = StackFrame {
            metaline: MetaLine::parse(r#".div .. data-empty="""#, 0).unwrap(),
            child_index: 0,
        };

        assert!(
            matches_full(&[element], &selector),
            "Should match empty attribute value"
        );
    }

    #[test]
    fn test_matching_with_special_characters_in_values() {
        let test_cases = vec![
            (r#"[data-test="a>b"]"#, r#".div .. data-test="a>b""#),
            (r#"[data-test="a<b"]"#, r#".div .. data-test="a<b""#),
            (r#"[data-test="a&b"]"#, r#".div .. data-test="a&b""#),
            (r#"[data-test="a=b"]"#, r#".div .. data-test="a=b""#),
            (r#"[data-test="a b"]"#, r#".div .. data-test="a b""#),
            (
                r#"[data-test="line1\nline2"]"#,
                r#".div .. data-test="line1\nline2""#,
            ),
        ];

        for (selector_str, metaline_str) in test_cases {
            let selector = match parse_selector(selector_str) {
                Ok(s) => s,
                Err(_) => continue, // Skip if parsing fails
            };

            let element = StackFrame {
                metaline: MetaLine::parse(metaline_str, 0).unwrap(),
                child_index: 0,
            };

            // We're testing that matching doesn't panic with special chars
            let _result = matches_full(&[element], &selector);
        }
    }

    #[test]
    fn test_matching_with_many_classes() {
        // Element with many classes
        let mut metaline_str = String::from(".div ..");
        for i in 0..100 {
            metaline_str.push_str(&format!(" .class{i}"));
        }

        let element = StackFrame {
            metaline: MetaLine::parse(&metaline_str, 0).unwrap(),
            child_index: 0,
        };

        // Selector with subset of classes
        let selector = parse_selector(".class1.class50.class99").unwrap();
        assert!(
            matches_full(std::slice::from_ref(&element), &selector),
            "Should match subset of many classes"
        );

        // Selector with non-existent class
        let selector = parse_selector(".class1.class50.class100").unwrap();
        assert!(
            !matches_full(&[element], &selector),
            "Should not match with non-existent class"
        );
    }

    #[test]
    fn test_matching_deeply_nested_not() {
        // Create a deeply nested :not() selector
        let selector = parse_selector("div:not(.a):not(.b):not(.c):not(.d):not(.e)").unwrap();

        // Element that doesn't have any of these classes
        let matching_element = StackFrame {
            metaline: MetaLine::parse(".div .. .other", 0).unwrap(),
            child_index: 0,
        };
        assert!(matches_full(&[matching_element], &selector));

        // Element with one of the excluded classes
        let non_matching_element = StackFrame {
            metaline: MetaLine::parse(".div .. .other .c", 0).unwrap(),
            child_index: 0,
        };
        assert!(!matches_full(&[non_matching_element], &selector));
    }

    #[test]
    fn test_matching_with_extreme_stack_depth() {
        // Create a very deep stack
        let mut stack = Vec::new();
        for i in 0..1000 {
            stack.push(StackFrame {
                metaline: MetaLine::parse(&format!(".level{i}"), 0).unwrap(),
                child_index: i % 10,
            });
        }

        // Simple selector at the end
        let selector = parse_selector("level999").unwrap();
        assert!(matches_full(&stack, &selector));

        // Deep descendant selector
        let selector = parse_selector("level0 level999").unwrap();
        assert!(matches_full(&stack, &selector));

        // Child combinator shouldn't match (not direct parent-child)
        let selector = parse_selector("level0 > level999").unwrap();
        assert!(!matches_full(&stack, &selector));
    }

    #[test]
    fn test_matching_with_id_edge_cases() {
        // Test various ID formats and edge cases
        let test_cases = vec![
            ("#test", ":test", true),
            ("#test", ":other", false),
            ("#123", ":123", true), // Now supported!
            ("#with-dash", ":with-dash", true),
            ("#with_underscore", ":with_underscore", true),
            ("#спец", ":спец", true), // Cyrillic - now works!
            ("#test", "", false),     // No ID (auto-generated numeric)
        ];

        for (selector_str, id_in_metaline, should_match) in test_cases {
            let selector = match parse_selector(selector_str) {
                Ok(s) => s,
                Err(_) => continue,
            };

            let metaline_str = if id_in_metaline.is_empty() {
                ".div".to_string()
            } else {
                format!(".div {id_in_metaline}")
            };

            let element = StackFrame {
                metaline: MetaLine::parse(&metaline_str, 0).unwrap(),
                child_index: 0,
            };

            assert_eq!(
                matches_full(&[element], &selector),
                should_match,
                "ID selector {selector_str} on element with ID {id_in_metaline}"
            );
        }
    }

    #[test]
    fn test_matching_nth_child_boundary_cases() {
        let test_cases = vec![
            (":nth-child(1)", 0, true),            // First child (0-indexed)
            (":nth-child(1)", 1, false),           // Second child
            (":nth-child(0)", 0, false),           // Invalid nth-child(0)
            (":nth-child(1000000)", 999999, true), // Large index
            (":first-child", 0, true),
            (":first-child", 1, false),
        ];

        for (selector_str, child_index, should_match) in test_cases {
            let selector = match parse_selector(selector_str) {
                Ok(s) => s,
                Err(_) => continue,
            };

            let element = StackFrame {
                metaline: MetaLine::parse(".p", 0).unwrap(),
                child_index,
            };

            assert_eq!(
                matches_full(&[element], &selector),
                should_match,
                "{selector_str} at index {child_index}"
            );
        }
    }

    #[test]
    fn test_matching_multiple_same_attributes() {
        // Element with same attribute specified multiple times (unusual but possible)
        let element = StackFrame {
            metaline: MetaLine::parse(r#".div .. role=button role=navigation"#, 0).unwrap(),
            child_index: 0,
        };

        // Should match the first occurrence
        let selector = parse_selector(r#"[role=button]"#).unwrap();
        assert!(matches_full(&[element], &selector));
    }

    #[test]
    fn test_selector_with_all_features() {
        // Combine everything in one selector
        let complex_selector = r#"article#main.highlight.featured:first-child:not(.draft)[lang=en][dir=ltr] > section.content > p.lead"#;

        let selector = match parse_selector(complex_selector) {
            Ok(s) => s,
            Err(e) => {
                println!("Complex selector failed to parse: {e}");
                return;
            }
        };

        // Build matching stack
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(
                    r#".article .. .highlight .featured lang=en dir=ltr :main"#,
                    0,
                )
                .unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section .. .content", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".p .. .lead", 0).unwrap(),
                child_index: 0,
            },
        ];

        assert!(
            matches_full(&stack, &selector),
            "Complex selector should match"
        );
    }

    #[test]
    fn test_performance_stress_test() {
        // Create a selector with many conditions
        let mut classes = vec![];
        for i in 0..50 {
            classes.push(format!(".class{i}"));
        }
        let selector_str = format!("div{}", classes.join(""));

        let selector = parse_selector(&selector_str).unwrap();

        // Create matching element
        let mut metaline_str = String::from(".div ..");
        for i in 0..50 {
            metaline_str.push_str(&format!(" .class{i}"));
        }
        let element = StackFrame {
            metaline: MetaLine::parse(&metaline_str, 0).unwrap(),
            child_index: 0,
        };

        // This should complete quickly despite complexity
        assert!(matches_full(&[element], &selector));
    }

    #[test]
    fn test_metaline_parsing_edge_cases() {
        // Test edge cases in MetaLine parsing that affect selectors
        let edge_cases = vec![
            r#".div .. :123"#,     // Numeric ID
            r#".div .. : "#,       // Empty ID after colon
            r#".div .. ::"#,       // Double colon
            r#".div .. . "#,       // Empty class
            r#".div .. .."#,       // Double dots at end
            r#".div key= "#,       // Empty value after =
            r#".div =value"#,      // Missing key
            r#".div "key=value""#, // Quoted key=value
            r#".div \"escaped\""#, // Escaped quotes
        ];

        for metaline_str in edge_cases {
            // Test that parsing doesn't panic
            let _ = MetaLine::parse(metaline_str, 0);
        }
    }
}
