//! Integration tests for the selector parsing and matching system
//!
//! These tests ensure that the parser and AST work together correctly
//! and handle various edge cases and error conditions.

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

    // ==============================================================================
    // Parser-AST Integration Tests
    // ==============================================================================

    #[test]
    fn test_parse_and_match_integration() {
        // Test that parsed selectors work correctly with the matching logic
        let test_cases = vec![
            // (selector_string, metaline, should_match)
            ("div", ".div", true),
            ("div", ".paragraph", false),
            ("#main", ".div :main", true),
            ("#main", ".div :other", false),
            (".highlight", ".p .. .highlight", true),
            (".highlight", ".p .. .normal", false),
            ("[disabled]", ".button .. disabled", true),
            ("[disabled]", ".button .. enabled", false),
            ("[role=navigation]", ".nav .. role=navigation", true),
            ("[role=navigation]", ".nav .. role=menu", false),
            ("div.container", ".div .. .container", true),
            ("div.container", ".div .. .wrapper", false),
            ("div#main.container", ".div .. .container :main", true),
            ("div#main.container", ".div .. .container :other", false),
            (":nth-child(1)", ".div", true),  // First child
            (":nth-child(2)", ".div", false), // Not second child
            (":first-child", ".div", true),
            ("div:not(.hidden)", ".div .. .visible", true),
            ("div:not(.hidden)", ".div .. .hidden", false),
        ];

        for (selector_str, metaline_str, should_match) in test_cases {
            let selector_group = parse_selector(selector_str)
                .unwrap_or_else(|e| panic!("Failed to parse '{selector_str}': {e}"));

            let stack = vec![StackFrame {
                metaline: MetaLine::parse(metaline_str, 0)
                    .unwrap_or_else(|| panic!("Failed to parse metaline '{metaline_str}'")),
                child_index: 0,
            }];

            let result = matches_full(&stack, &selector_group);
            assert_eq!(
                result, should_match,
                "Selector '{selector_str}' on element '{metaline_str}' - expected {should_match}, got {result}"
            );
        }
    }

    #[test]
    fn test_parse_and_match_descendant_selectors() {
        let test_cases = vec![
            // (selector, stack_metalines, should_match)
            ("body div", vec![".body", ".div"], true),
            (
                "body div",
                vec![".body", ".section", ".div"],
                true, // Still matches - descendant
            ),
            ("body > div", vec![".body", ".div"], true),
            (
                "body > div",
                vec![".body", ".section", ".div"],
                false, // Doesn't match - not direct child
            ),
            (
                "section div.highlight",
                vec![".document", ".section", ".article", ".div .. .highlight"],
                true,
            ),
            (
                "section > div.highlight",
                vec![".document", ".section", ".div .. .highlight"],
                true,
            ),
            (
                "section > div.highlight",
                vec![".document", ".section", ".article", ".div .. .highlight"],
                false,
            ),
        ];

        for (selector_str, metaline_strs, should_match) in test_cases {
            let selector_group = parse_selector(selector_str)
                .unwrap_or_else(|e| panic!("Failed to parse '{selector_str}': {e}"));

            let stack: Vec<StackFrame> = metaline_strs
                .into_iter()
                .enumerate()
                .map(|(i, ml_str)| StackFrame {
                    metaline: MetaLine::parse(ml_str, 0).unwrap(),
                    child_index: i,
                })
                .collect();

            let result = matches_full(&stack, &selector_group);
            assert_eq!(
                result, should_match,
                "Selector '{selector_str}' - expected {should_match}, got {result}"
            );
        }
    }

    #[test]
    fn test_parse_and_match_complex_selectors() {
        // Test complex real-world selectors
        let selector_str = "article > section.important paragraph.highlight:not(.draft)[lang=en]";
        let selector_group = parse_selector(selector_str).unwrap();

        // Matching case
        let matching_stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".article", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section .. .important", 0).unwrap(),
                child_index: 1,
            },
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph .. .highlight lang=en", 0).unwrap(),
                child_index: 2,
            },
        ];
        assert!(matches_full(&matching_stack, &selector_group));

        // Non-matching cases
        let non_matching_cases = vec![
            // Wrong structure - section not direct child of article
            vec![
                StackFrame {
                    metaline: MetaLine::parse(".article", 0).unwrap(),
                    child_index: 0,
                },
                StackFrame {
                    metaline: MetaLine::parse(".wrapper", 0).unwrap(),
                    child_index: 0,
                },
                StackFrame {
                    metaline: MetaLine::parse(".section .. .important", 0).unwrap(),
                    child_index: 0,
                },
                StackFrame {
                    metaline: MetaLine::parse(".paragraph .. .highlight lang=en", 0).unwrap(),
                    child_index: 0,
                },
            ],
            // Has .draft class (excluded by :not())
            vec![
                StackFrame {
                    metaline: MetaLine::parse(".article", 0).unwrap(),
                    child_index: 0,
                },
                StackFrame {
                    metaline: MetaLine::parse(".section .. .important", 0).unwrap(),
                    child_index: 1,
                },
                StackFrame {
                    metaline: MetaLine::parse(".paragraph .. .highlight .draft lang=en", 0)
                        .unwrap(),
                    child_index: 0,
                },
            ],
            // Wrong language
            vec![
                StackFrame {
                    metaline: MetaLine::parse(".article", 0).unwrap(),
                    child_index: 0,
                },
                StackFrame {
                    metaline: MetaLine::parse(".section .. .important", 0).unwrap(),
                    child_index: 1,
                },
                StackFrame {
                    metaline: MetaLine::parse(".paragraph .. .highlight lang=fr", 0).unwrap(),
                    child_index: 0,
                },
            ],
        ];

        for stack in non_matching_cases {
            assert!(!matches_full(&stack, &selector_group));
        }
    }

    #[test]
    fn test_parser_error_handling() {
        // Test various invalid selectors
        let invalid_selectors = vec![
            ("", "Empty selector"),
            ("   ", "Whitespace only"),
            (">", "Starts with combinator"),
            ("div >", "Ends with combinator"),
            ("div > > p", "Double combinator"),
            ("#", "Empty ID"),
            (".", "Empty class"),
            ("[]", "Empty attribute"),
            ("[=value]", "Attribute without key"),
            // ("[key=]", "Attribute without value"), // Now supported for edge cases
            (":nth-child()", "Empty nth-child"),
            (":nth-child(abc)", "Invalid nth-child value"),
            (":not()", "Empty not selector"),
            ("div:unknown-pseudo", "Unknown pseudo-class"),
            ("div..container", "Double dot"),
            ("div##main", "Double hash"),
            ("[key==value]", "Double equals"),
            ("div > + p", "Invalid combinator sequence"),
        ];

        for (selector_str, description) in invalid_selectors {
            let result = parse_selector(selector_str);
            assert!(
                result.is_err(),
                "Selector '{selector_str}' ({description}) should fail to parse, but got: {result:?}"
            );
        }
    }

    #[test]
    fn test_quoted_identifier_edge_cases() {
        // Test various quoted identifier edge cases
        let test_cases = vec![
            (r#""simple""#, "simple"),
            (r#""with spaces""#, "with spaces"),
            (r#""with \"quotes\"""#, r#"with "quotes""#),
            (r#""with \\backslash\\""#, r"with \backslash\"),
            // Parser doesn't currently support \n and \t escape sequences
            // (r#""with\nnewline""#, "with\nnewline"),
            // (r#""with\ttab""#, "with\ttab"),
            (r#""emoji 🎉""#, "emoji 🎉"),
            (r#""special!@#$%^&*()""#, "special!@#$%^&*()"),
        ];

        for (quoted, expected) in test_cases {
            let selector_str = format!(".{quoted}");
            let result = parse_selector(&selector_str);

            match result {
                Ok(group) => {
                    if let SelectorPart::Simple(ref selector) = group.selectors[0] {
                        assert!(
                            selector.classes.contains(expected),
                            "Expected class '{expected}' from quoted identifier '{quoted}'"
                        );
                    }
                }
                Err(e) => panic!("Failed to parse quoted identifier '{quoted}': {e}"),
            }
        }
    }

    #[test]
    fn test_selector_specificity_order() {
        // Test that more specific selectors take precedence in matching
        let element = StackFrame {
            metaline: MetaLine::parse(".div .. .container .highlight role=navigation :main", 0)
                .unwrap(),
            child_index: 0,
        };
        let stack = vec![element.clone()];

        // All these selectors should match
        let selectors = vec![
            "div",                                           // Type only
            "#main",                                         // ID only
            ".container",                                    // Single class
            "[role]",                                        // Attribute presence
            "div#main",                                      // Type + ID
            "div.container",                                 // Type + class
            "div[role]",                                     // Type + attribute
            "#main.container",                               // ID + class
            ".container.highlight",                          // Multiple classes
            "div#main.container",                            // Type + ID + class
            "div#main.container.highlight",                  // Type + ID + multiple classes
            "div#main.container.highlight[role]",            // Everything
            "div#main.container.highlight[role=navigation]", // Everything with value
        ];

        for selector_str in selectors {
            let selector_group = parse_selector(selector_str).unwrap();
            assert!(
                matches_full(&stack, &selector_group),
                "Selector '{selector_str}' should match"
            );
        }
    }

    #[test]
    fn test_pseudo_class_combinations() {
        // Test combining multiple pseudo-classes
        let test_cases = vec![
            (":first-child:not(.hidden)", ".div", 0, true),
            (":first-child:not(.hidden)", ".div .. .hidden", 0, false),
            (":nth-child(2):not(.draft)", ".paragraph", 1, true),
            (
                ":nth-child(2):not(.draft)",
                ".paragraph .. .draft",
                1,
                false,
            ),
        ];

        for (selector_str, metaline_str, child_index, should_match) in test_cases {
            let selector_group = parse_selector(selector_str).unwrap();
            let stack = vec![StackFrame {
                metaline: MetaLine::parse(metaline_str, 0).unwrap(),
                child_index,
            }];

            assert_eq!(
                matches_full(&stack, &selector_group),
                should_match,
                "Selector '{selector_str}' on '{metaline_str}' at index {child_index} - expected {should_match}"
            );
        }
    }

    #[test]
    fn test_attribute_selector_variants_parsing() {
        // Test that attribute selector variants are parsed correctly
        let variants = vec![
            ("[attr]", "Presence"),
            ("[!attr]", "Negated presence"),
            ("[key=value]", "Exact match"),
            ("[key~=value]", "Negated match"),
            ("[key^=value]", "Starts with"),
            ("[key$=value]", "Ends with"),
            ("[key*=value]", "Contains"),
        ];

        for (selector_str, description) in variants {
            let result = parse_selector(selector_str);
            assert!(
                result.is_ok(),
                "Failed to parse {description} selector: {selector_str}"
            );
        }
    }

    #[test]
    fn test_sibling_combinators() {
        // Test sibling combinators (currently not fully implemented in matching)
        let adjacent = parse_selector("h1 + p").unwrap();
        let general = parse_selector("h1 ~ p").unwrap();

        // Verify they parse correctly
        assert_eq!(adjacent.selectors.len(), 3);
        assert_eq!(
            adjacent.selectors[1],
            SelectorPart::Combinator(Combinator::AdjacentSibling)
        );

        assert_eq!(general.selectors.len(), 3);
        assert_eq!(
            general.selectors[1],
            SelectorPart::Combinator(Combinator::GeneralSibling)
        );

        // Test that matching returns false for sibling combinators (not implemented)
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".h1", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".p", 0).unwrap(),
                child_index: 1,
            },
        ];

        assert!(
            !matches_full(&stack, &adjacent),
            "Adjacent sibling combinator not implemented"
        );
        assert!(
            !matches_full(&stack, &general),
            "General sibling combinator not implemented"
        );
    }

    #[test]
    fn test_stress_deeply_nested_selectors() {
        // Create a very deep selector chain
        let mut selector_parts = vec![];
        for i in 0..50 {
            if i > 0 {
                selector_parts.push(" > ".to_string());
            }
            selector_parts.push(format!("level{i}"));
        }
        let deep_selector_str = selector_parts.join("");

        // This should parse successfully
        let result = parse_selector(&deep_selector_str);
        assert!(result.is_ok(), "Should handle deeply nested selectors");

        let selector_group = result.unwrap();
        // Should have 50 simple selectors and 49 combinators
        assert_eq!(selector_group.selectors.len(), 99);
    }

    #[test]
    fn test_unicode_identifiers() {
        // Test Unicode in identifiers
        let unicode_selectors = vec![
            ".日本語",
            "#Ñoño",
            r#"[data-emoji="🎉"]"#,
            ".café",
            "#naïve",
            r#"."класс с пробелами""#,
        ];

        for selector_str in unicode_selectors {
            let result = parse_selector(selector_str);
            assert!(
                result.is_ok(),
                "Should handle Unicode selector: {selector_str}"
            );
        }
    }

    #[test]
    #[ignore = "slow"] // Slow test: takes 210ms. Run with --ignored to include.
    fn test_performance_many_classes() {
        // Test selector with many classes
        let mut classes = vec![];
        for i in 0..100 {
            classes.push(format!(".class{i}"));
        }
        let many_classes_selector = classes.join("");

        let result = parse_selector(&many_classes_selector);
        assert!(result.is_ok(), "Should handle many classes");

        if let Ok(group) = result {
            if let SelectorPart::Simple(ref selector) = group.selectors[0] {
                assert_eq!(selector.classes.len(), 100);
            }
        }
    }

    #[test]
    fn test_malformed_not_selectors() {
        // Test various malformed :not() selectors
        let malformed = vec![
            "div:not",          // Missing parentheses
            "div:not(",         // Unclosed parenthesis
            "div:not(())",      // Double parentheses
            "div:not(:not(p))", // Nested :not() - actually valid in CSS4
            "div:not(> p)",     // Starting with combinator
            "div:not(p >)",     // Ending with combinator
        ];

        for selector_str in malformed {
            let result = parse_selector(selector_str);
            // Some of these might actually parse, but let's verify they don't crash
            match result {
                Ok(_) => println!("Selector '{selector_str}' parsed successfully"),
                Err(e) => println!("Selector '{selector_str}' failed as expected: {e}"),
            }
        }
    }

    #[test]
    fn test_empty_document_edge_cases() {
        // Test matching against empty or minimal documents
        let selectors = vec![
            "div",
            "body div",
            "div > p",
            "*",
            ":first-child",
            "[any-attr]",
        ];

        for selector_str in selectors {
            let selector_group = parse_selector(selector_str).unwrap();

            // Empty stack
            assert!(
                !matches_full(&[], &selector_group),
                "Should not match empty stack"
            );

            // Single element stack
            let single = vec![StackFrame {
                metaline: MetaLine::parse(".root", 0).unwrap(),
                child_index: 0,
            }];

            let should_match = selector_str == "*" || selector_str == ":first-child";
            assert_eq!(
                matches_full(&single, &selector_group),
                should_match,
                "Selector '{selector_str}' on single element"
            );
        }
    }

    #[test]
    fn test_child_index_tracking_with_nth_child() {
        // Test proper child index tracking for nth-child selectors
        let test_cases = vec![
            (":nth-child(1)", 0, true),
            (":nth-child(1)", 1, false),
            (":nth-child(2)", 0, false),
            (":nth-child(2)", 1, true),
            (":nth-child(3)", 2, true),
            (":nth-child(10)", 9, true),
            (":nth-child(10)", 10, false),
        ];

        for (selector_str, child_index, should_match) in test_cases {
            let selector_group = parse_selector(selector_str).unwrap();
            let stack = vec![StackFrame {
                metaline: MetaLine::parse(".paragraph", 0).unwrap(),
                child_index,
            }];

            assert_eq!(
                matches_full(&stack, &selector_group),
                should_match,
                "Selector '{selector_str}' with child_index {child_index} - expected {should_match}"
            );
        }
    }

    #[test]
    fn test_complex_child_index_scenarios() {
        // Test child indices in nested structures
        let selector = parse_selector("section:nth-child(2) > paragraph:first-child").unwrap();

        // Matching case: second section with first paragraph
        let matching_stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".document", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 1, // Second child
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 0).unwrap(),
                child_index: 0, // First child
            },
        ];
        assert!(matches_full(&matching_stack, &selector));

        // Non-matching: first section
        let non_matching_stack1 = vec![
            StackFrame {
                metaline: MetaLine::parse(".document", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 0, // First child, not second
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 0).unwrap(),
                child_index: 0,
            },
        ];
        assert!(!matches_full(&non_matching_stack1, &selector));

        // Non-matching: second paragraph instead of first
        let non_matching_stack2 = vec![
            StackFrame {
                metaline: MetaLine::parse(".document", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 1,
            },
            StackFrame {
                metaline: MetaLine::parse(".paragraph", 0).unwrap(),
                child_index: 1, // Second child, not first
            },
        ];
        assert!(!matches_full(&non_matching_stack2, &selector));
    }

    #[test]
    fn test_child_combinator_with_indices() {
        // Test that child combinator respects both structure and indices
        let selector = parse_selector("ul > li:nth-child(3)").unwrap();

        // Direct child relationship
        let direct_child = vec![
            StackFrame {
                metaline: MetaLine::parse(".ul", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".li", 0).unwrap(),
                child_index: 2, // Third child
            },
        ];
        assert!(matches_full(&direct_child, &selector));

        // Not a direct child (has intermediate element)
        let not_direct = vec![
            StackFrame {
                metaline: MetaLine::parse(".ul", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".div", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".li", 0).unwrap(),
                child_index: 2,
            },
        ];
        assert!(!matches_full(&not_direct, &selector));
    }

    #[test]
    fn test_first_child_vs_nth_child_equivalence() {
        // :first-child should be equivalent to :nth-child(1)
        let first_child = parse_selector(":first-child").unwrap();
        let nth_child_1 = parse_selector(":nth-child(1)").unwrap();

        let test_stacks = vec![
            vec![StackFrame {
                metaline: MetaLine::parse(".p", 0).unwrap(),
                child_index: 0,
            }],
            vec![StackFrame {
                metaline: MetaLine::parse(".p", 0).unwrap(),
                child_index: 1,
            }],
            vec![StackFrame {
                metaline: MetaLine::parse(".p", 0).unwrap(),
                child_index: 5,
            }],
        ];

        for stack in test_stacks {
            let first_match = matches_full(&stack, &first_child);
            let nth_match = matches_full(&stack, &nth_child_1);
            assert_eq!(
                first_match, nth_match,
                ":first-child and :nth-child(1) should match identically"
            );
        }
    }

    #[test]
    fn test_mixed_structural_and_index_selectors() {
        // Complex selector mixing structural and index-based selection
        let selector =
            parse_selector("article section:nth-child(2) > div.container > p:first-child").unwrap();

        let matching_stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".article", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section", 0).unwrap(),
                child_index: 1, // Second section
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .container", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".p", 0).unwrap(),
                child_index: 0, // First paragraph
            },
        ];
        assert!(matches_full(&matching_stack, &selector));
    }

    #[test]
    fn test_nth_child_with_not_selector() {
        // Combining nth-child with :not()
        let selector = parse_selector("li:nth-child(2):not(.hidden)").unwrap();

        // Second child, not hidden
        let matching = vec![StackFrame {
            metaline: MetaLine::parse(".li .. .visible", 0).unwrap(),
            child_index: 1,
        }];
        assert!(matches_full(&matching, &selector));

        // Second child, but hidden
        let not_matching = vec![StackFrame {
            metaline: MetaLine::parse(".li .. .hidden", 0).unwrap(),
            child_index: 1,
        }];
        assert!(!matches_full(&not_matching, &selector));

        // Not second child
        let wrong_index = vec![StackFrame {
            metaline: MetaLine::parse(".li .. .visible", 0).unwrap(),
            child_index: 0,
        }];
        assert!(!matches_full(&wrong_index, &selector));
    }

    #[test]
    fn test_zero_index_edge_case() {
        // Test that child_index 0 works correctly
        let first_child_selector = parse_selector(":first-child").unwrap();
        let nth_child_0_selector = parse_selector(":nth-child(0)").unwrap(); // Invalid in CSS but let's test

        let zero_index = vec![StackFrame {
            metaline: MetaLine::parse(".element", 0).unwrap(),
            child_index: 0,
        }];

        assert!(matches_full(&zero_index, &first_child_selector));
        // nth-child(0) should not match - it's invalid in CSS
        assert!(!matches_full(&zero_index, &nth_child_0_selector));
    }

    #[test]
    fn test_large_child_indices() {
        // Test with very large child indices
        let selector = parse_selector(":nth-child(1000000)").unwrap();

        let large_index = vec![StackFrame {
            metaline: MetaLine::parse(".element", 0).unwrap(),
            child_index: 999999, // 1000000th child (0-indexed)
        }];
        assert!(matches_full(&large_index, &selector));

        let not_matching = vec![StackFrame {
            metaline: MetaLine::parse(".element", 0).unwrap(),
            child_index: 999998,
        }];
        assert!(!matches_full(&not_matching, &selector));
    }
}
