//! CSS-like Selector Demo for Wasot
//!
//! This demo shows how CSS-like selectors work with Wasot documents.
//! It demonstrates:
//! - Parsing selector strings into AST structures
//! - Matching logic for selectors against document elements
//! - Support for combinators (descendant, child)
//! - Support for various selector types (type, ID, class, attributes, :not())
//! - How selectors work with a document stack to track hierarchy
//!
//! The AST structures are defined in `src/parsers/selectors/ast.rs`
//! The parser is defined in `src/parsers/selectors/parser.rs`

use wasot_compiler::parsers::selectors::{
    matches_prefix, parse_selector, SimpleSelector, SimpleSelectorGroup, StackFrame,
};
use wasot_compiler::MetaLine;

// ==============================================================================
// Main Function for Live Demonstration
// ==============================================================================

fn main() {
    println!("🚀 CSS-like Selector Parser and Matcher Demo for Wasot\n");
    println!("{}", "=".repeat(60));

    // ==============================================================================
    // Part 1: Parsing selectors from strings
    // ==============================================================================
    println!("\n📝 Part 1: Parsing Selectors from Strings");
    println!("{}", "-".repeat(40));

    // Example 1: Simple selector
    let selector_str = "div.container#main";
    println!("\nParsing: '{selector_str}'");
    match parse_selector(selector_str) {
        Ok(selector_group) => {
            println!("✅ Successfully parsed!");
            println!("   Result: {:?}", selector_group.selectors);
        }
        Err(e) => println!("❌ Parse error: {e}"),
    }

    // Example 2: Selector with combinator
    let selector_str = "section > p.highlight";
    println!("\nParsing: '{selector_str}'");
    match parse_selector(selector_str) {
        Ok(selector_group) => {
            println!("✅ Successfully parsed!");
            println!("   Result: {} parts", selector_group.selectors.len());
        }
        Err(e) => println!("❌ Parse error: {e}"),
    }

    // Example 3: Complex selector with :not()
    let selector_str = "div:not(.hidden)[role=main]";
    println!("\nParsing: '{selector_str}'");
    match parse_selector(selector_str) {
        Ok(_selector_group) => {
            println!("✅ Successfully parsed!");
        }
        Err(e) => println!("❌ Parse error: {e}"),
    }

    // Example 4: Selector with quoted identifier (spaces in names)
    let selector_str = r#"."class with spaces""#;
    println!("\nParsing: '{selector_str}'");
    match parse_selector(selector_str) {
        Ok(_selector_group) => {
            println!("✅ Successfully parsed!");
        }
        Err(e) => println!("❌ Parse error: {e}"),
    }

    // ==============================================================================
    // Part 2: Using parsed selectors for matching
    // ==============================================================================
    println!("\n\n🎯 Part 2: Using Parsed Selectors for Matching");
    println!("{}", "-".repeat(40));

    // Create a document stack to match against
    let stack = vec![
        StackFrame {
            metaline: MetaLine::parse(".body", 0).expect("Failed to parse body"),
            child_index: 0,
        },
        StackFrame {
            metaline: MetaLine::parse(".section .. .main-content", 0)
                .expect("Failed to parse section"),
            child_index: 0,
        },
        StackFrame {
            metaline: MetaLine::parse(".div .. .red .container data-active=true :main", 0)
                .expect("Failed to parse div"),
            child_index: 1,
        },
    ];

    println!("\nDocument structure:");
    println!("  body");
    println!("    section.main-content");
    println!("      div.red.container#main[data-active=true]");

    // Test various selectors against this stack
    let test_selectors = vec![
        ("div", true, "matches any div"),
        ("div.container", true, "matches div with container class"),
        ("div#main", true, "matches div with id=main"),
        (
            "section > div",
            true,
            "matches div that is child of section",
        ),
        ("body div", true, "matches div descendant of body"),
        ("div.missing", false, "doesn't match - missing class"),
        ("p", false, "doesn't match - wrong element type"),
        ("body > div", false, "doesn't match - not direct child"),
        (
            "div[data-active]",
            true,
            "matches div with data-active attribute",
        ),
        (
            "div:nth-child(2)",
            true,
            "matches as second child (index 1)",
        ),
        (
            "div:not(.hidden)",
            true,
            "matches - doesn't have .hidden class",
        ),
        (
            "section.main-content > div.red",
            true,
            "complex selector match",
        ),
    ];

    println!("\nTesting selectors:");
    for (selector_str, expected, description) in test_selectors {
        print!("  '{selector_str}' - {description}: ");

        match parse_selector(selector_str) {
            Ok(selector_group) => {
                let result = matches_prefix(&stack, &selector_group);
                if result == expected {
                    println!("✅ PASS");
                } else {
                    println!("❌ FAIL (expected {expected}, got {result})");
                }
            }
            Err(e) => println!("❌ Parse error: {e}"),
        }
    }

    // ==============================================================================
    // Part 3: Demonstrating manual AST construction vs parsing
    // ==============================================================================
    println!("\n\n🔧 Part 3: Manual AST Construction vs String Parsing");
    println!("{}", "-".repeat(40));

    // Manual construction using builder pattern
    println!("\nManual construction: div.red#main[data-active]");
    let manual_selector = SimpleSelectorGroup {
        selectors: vec![SimpleSelector::builder()
            .tag("div")
            .class("red")
            .id("main")
            .attribute("data-active")
            .part()],
    };

    // Parse the same selector from string
    println!("String parsing:      div.red#main[data-active]");
    let parsed_selector = parse_selector("div.red#main[data-active]").unwrap();

    // Both should match our element
    let result_manual = matches_prefix(&stack, &manual_selector);
    let result_parsed = matches_prefix(&stack, &parsed_selector);

    println!("\nResults:");
    println!(
        "  Manual construction: {}",
        if result_manual {
            "✅ Match"
        } else {
            "❌ No match"
        }
    );
    println!(
        "  String parsing:      {}",
        if result_parsed {
            "✅ Match"
        } else {
            "❌ No match"
        }
    );

    if result_manual == result_parsed {
        println!("\n✅ Both methods produce equivalent results!");
    } else {
        println!("\n❌ Results differ - there may be an issue!");
    }

    // ==============================================================================
    // Part 4: Advanced :not() pseudo-selector demonstration
    // ==============================================================================
    println!("\n\n🔍 Part 4: Advanced :not() Pseudo-Selector");
    println!("{}", "-".repeat(40));

    // Test parsing and using :not() with string selectors
    let test_cases = vec![
        (
            "div:not(.active)",
            ".div .. .item id=item1",
            true,
            "matches div without .active",
        ),
        (
            "div:not(.active)",
            ".div .. .item .active id=item2",
            false,
            "doesn't match div with .active",
        ),
        (
            "div:not([disabled])",
            ".div .. enabled=true",
            true,
            "matches div without disabled attribute",
        ),
        (
            "p:not(#special)",
            ".p .. id=normal",
            true,
            "matches p without id=special",
        ),
    ];

    println!("\nTesting :not() selectors:");
    for (selector_str, element_str, expected, description) in test_cases {
        print!("  {selector_str} - {description}: ");

        let parsed = parse_selector(selector_str);
        let element = vec![StackFrame {
            metaline: MetaLine::parse(element_str, 0).unwrap(),
            child_index: 0,
        }];

        match parsed {
            Ok(selector_group) => {
                let result = matches_prefix(&element, &selector_group);
                if result == expected {
                    println!("✅ PASS");
                } else {
                    println!("❌ FAIL (expected {expected}, got {result})");
                }
            }
            Err(e) => println!("❌ Parse error: {e}"),
        }
    }

    println!("\n\n");
    println!("🎉 Demo completed successfully!");
    println!("{}", "=".repeat(60));
}

// ==============================================================================
// Unit Test Module
// ==============================================================================
#[cfg(test)]
mod tests {
    use wasot_compiler::parsers::selectors::{Combinator, SelectorPart};

    use super::*;

    #[test]
    fn test_compound_simple_selector() {
        let compound_selector = SimpleSelector::builder()
            .tag("div")
            .id("main")
            .class("red")
            .attribute("data-active")
            .build();

        // A perfect match
        let element_match = StackFrame {
            metaline: MetaLine::parse(".div .. .red .container data-active=true :main", 0)
                .expect("Failed to parse div"),
            child_index: 0,
        };
        assert!(
            compound_selector.matches(&element_match),
            "Should match when all conditions are met"
        );

        // Fails because of wrong tag
        let mut element_wrong_tag = element_match.clone();
        element_wrong_tag.metaline =
            MetaLine::parse(".p .. .red .container id=main data-active=true", 0)
                .expect("Failed to parse p");
        assert!(
            !compound_selector.matches(&element_wrong_tag),
            "Should fail on wrong tag"
        );

        // Fails because of missing ID
        let element_no_id = StackFrame {
            metaline: MetaLine::parse(".div .. .red .container data-active=true", 0)
                .expect("Failed to parse div"),
            child_index: 0,
        };
        assert!(
            !compound_selector.matches(&element_no_id),
            "Should fail on missing id"
        );

        // Fails because of missing class
        let element_no_class = StackFrame {
            metaline: MetaLine::parse(".div .. .container data-active=true :main", 0)
                .expect("Failed to parse div"),
            child_index: 0,
        };
        assert!(
            !compound_selector.matches(&element_no_class),
            "Should fail on missing class"
        );
    }

    #[test]
    fn test_not_pseudo_selector() {
        // Element to be tested against: <div class="item promoted" id="main">
        let element = StackFrame {
            metaline: MetaLine::parse(".div .. .item .promoted :main", 0).unwrap(),
            child_index: 0,
        };

        // 1. Should match `div:not(.disabled)` because it doesn't have .disabled
        let selector_not_disabled = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::builder().class("disabled").build())
            .build();
        assert!(
            selector_not_disabled.matches(&element),
            "Should match: div:not(.disabled)"
        );

        // 2. Should NOT match `div:not(.promoted)` because it HAS .promoted
        let selector_not_promoted = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::builder().class("promoted").build())
            .build();
        assert!(
            !selector_not_promoted.matches(&element),
            "Should not match: div:not(.promoted)"
        );

        // 3. Should match `.item:not(#other-id)`. It has #main, so it is not #other-id.
        let selector_not_other_id = SimpleSelector::builder()
            .class("item")
            .not(SimpleSelector::builder().id("other-id").build())
            .build();
        assert!(
            selector_not_other_id.matches(&element),
            "Should match: .item:not(#other-id)"
        );

        // 4. Should NOT match `.item:not(#main)` because it HAS the id #main
        let selector_not_main_id = SimpleSelector::builder()
            .class("item")
            .not(SimpleSelector::builder().id("main").build())
            .build();
        assert!(
            !selector_not_main_id.matches(&element),
            "Should not match: .item:not(#main)"
        );

        // 5. Should NOT match because the outer part fails (wrong tag `p`)
        let selector_wrong_tag = SimpleSelector::builder()
            .tag("p")
            .not(SimpleSelector::builder().class("disabled").build())
            .build();
        assert!(
            !selector_wrong_tag.matches(&element),
            "Should not match due to wrong outer tag"
        );

        // 6. Should match because outer part matches, and inner `not` doesn't match the element.
        // Selector: `.item.promoted:not([data-ignore])`
        let selector_with_attr = SimpleSelector::builder()
            .class("item")
            .class("promoted")
            .not(SimpleSelector::builder().attribute("data-ignore").build())
            .build();
        assert!(
            selector_with_attr.matches(&element),
            "Should match complex selector with :not"
        );
    }

    #[test]
    fn test_matches_with_not_in_chain() {
        // Corresponds to: <body> <div class="item"> <p class="content">
        let stack_match = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .item", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".p .. .content", 0).unwrap(),
                child_index: 2,
            },
        ];

        // Corresponds to: <body> <div class="item modal"> <p class="content">
        let stack_no_match = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .item .modal", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".p .. .content", 0).unwrap(),
                child_index: 2,
            },
        ];

        // Selector: `div:not(.modal) > p`
        let selector_group = SimpleSelectorGroup {
            selectors: vec![
                SimpleSelector::builder()
                    .tag("div")
                    .not(SimpleSelector::builder().class("modal").build())
                    .part(),
                SelectorPart::Combinator(Combinator::Child),
                SimpleSelector::builder().tag("p").part(),
            ],
        };

        assert!(
            matches_prefix(&stack_match, &selector_group),
            "Should match chain with :not on the parent"
        );
        assert!(
            !matches_prefix(&stack_no_match, &selector_group),
            "Should not match chain because :not(.modal) fails"
        );
    }

    #[test]
    fn test_matches_with_compound_selector_in_chain() {
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".body", 0).expect("Failed to parse body"),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .wrapper :main", 0)
                    .expect("Failed to parse div"),
                child_index: 0,
            },
        ];

        // Selector: `body > div#main.wrapper`
        let selector = vec![
            SimpleSelector::builder().tag("body").part(),
            SelectorPart::Combinator(Combinator::Child),
            SimpleSelector::builder()
                .tag("div")
                .id("main")
                .class("wrapper")
                .part(),
        ];

        assert!(
            matches_prefix(
                &stack,
                &SimpleSelectorGroup {
                    selectors: selector
                }
            ),
            "Should match a compound selector in a chain"
        );
    }

    #[test]
    fn test_selector_parsing_various_formats() {
        // Test basic type selector
        let result = parse_selector("div");
        assert!(result.is_ok(), "Should parse basic type selector");

        // Test class selector
        let result = parse_selector(".container");
        assert!(result.is_ok(), "Should parse class selector");

        // Test ID selector
        let result = parse_selector("#main");
        assert!(result.is_ok(), "Should parse ID selector");

        // Test attribute selector
        let result = parse_selector("[data-role]");
        assert!(result.is_ok(), "Should parse attribute selector");

        // Test quoted class with spaces
        let result = parse_selector(r#"."class with spaces""#);
        assert!(result.is_ok(), "Should parse quoted class with spaces");

        // Test compound selector
        let result = parse_selector("div.container#main[data-active]");
        assert!(result.is_ok(), "Should parse compound selector");

        // Test selector with child combinator
        let result = parse_selector("section > p");
        assert!(
            result.is_ok(),
            "Should parse selector with child combinator"
        );

        // Test selector with descendant combinator
        let result = parse_selector("body div");
        assert!(
            result.is_ok(),
            "Should parse selector with descendant combinator"
        );

        // Test :not() pseudo-selector
        let result = parse_selector("div:not(.hidden)");
        assert!(result.is_ok(), "Should parse :not() pseudo-selector");

        // Test complex :not() with attribute
        let result = parse_selector("div:not([disabled])");
        assert!(result.is_ok(), "Should parse :not() with attribute");
    }

    #[test]
    fn test_selector_parsing_error_cases() {
        // Test invalid selector syntax
        let result = parse_selector("div..invalid");
        assert!(result.is_err(), "Should fail on invalid syntax");

        // Test unclosed quote
        let result = parse_selector(r#"."unclosed quote"#);
        assert!(result.is_err(), "Should fail on unclosed quote");

        // Test empty selector
        let result = parse_selector("");
        assert!(result.is_err(), "Should fail on empty selector");
    }

    #[test]
    fn test_stack_matching_edge_cases() {
        // Empty stack should not match anything
        let empty_stack = vec![];
        let selector = parse_selector("div").unwrap();
        assert!(
            !matches_prefix(&empty_stack, &selector),
            "Empty stack should not match"
        );

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

        let selector = parse_selector("div.container").unwrap();
        assert!(
            matches_prefix(&single_stack, &selector),
            "Should match single element"
        );

        let selector = parse_selector("p").unwrap();
        assert!(
            !matches_prefix(&single_stack, &selector),
            "Should not match wrong element"
        );
    }

    #[test]
    fn test_nth_child_pseudo_selector() {
        // Test various child indices
        let test_cases = vec![
            (0, "div:nth-child(1)", true),  // index 0 = first child
            (1, "div:nth-child(2)", true),  // index 1 = second child
            (2, "div:nth-child(3)", true),  // index 2 = third child
            (0, "div:nth-child(2)", false), // index 0 ≠ second child
            (5, "div:nth-child(6)", true),  // index 5 = sixth child
        ];

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

            let selector = parse_selector(selector_str).unwrap();
            let result = matches_prefix(&stack, &selector);
            assert_eq!(
                result, expected,
                "Child index {child_index} with selector '{selector_str}' should be {expected}"
            );
        }
    }

    #[test]
    fn test_attribute_selector_variations() {
        // Element with various attributes
        let element = StackFrame {
            metaline: MetaLine::parse(
                ".div .. data-role=button data-active=true disabled id=test",
                0,
            )
            .unwrap(),
            child_index: 0,
        };
        let stack = vec![element];

        // Test attribute existence
        let selector = parse_selector("div[data-role]").unwrap();
        assert!(
            matches_prefix(&stack, &selector),
            "Should match attribute existence"
        );

        // Test attribute value
        let selector = parse_selector("div[data-role=button]").unwrap();
        assert!(
            matches_prefix(&stack, &selector),
            "Should match attribute value"
        );

        // Test boolean attribute
        let selector = parse_selector("div[disabled]").unwrap();
        assert!(
            matches_prefix(&stack, &selector),
            "Should match boolean attribute"
        );

        // Test missing attribute
        let selector = parse_selector("div[missing]").unwrap();
        assert!(
            !matches_prefix(&stack, &selector),
            "Should not match missing attribute"
        );

        // Test wrong attribute value
        let selector = parse_selector("div[data-role=link]").unwrap();
        assert!(
            !matches_prefix(&stack, &selector),
            "Should not match wrong attribute value"
        );
    }

    #[test]
    fn test_complex_selector_chains() {
        // Create a deep document structure
        let stack = vec![
            StackFrame {
                metaline: MetaLine::parse(".html", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".body .. .main-layout", 0).unwrap(),
                child_index: 0,
            },
            StackFrame {
                metaline: MetaLine::parse(".section .. .sidebar", 0).unwrap(),
                child_index: 1,
            },
            StackFrame {
                metaline: MetaLine::parse(".div .. .widget .active data-type=menu", 0).unwrap(),
                child_index: 0,
            },
        ];

        // Test various complex selectors
        let test_cases = vec![
            ("html body section div", true, "deep descendant chain"),
            ("body > section", true, "direct child relationship"),
            ("html div.widget", true, "skip levels descendant"),
            ("section > div.active", true, "direct child with class"),
            (
                "body section.sidebar div[data-type=menu]",
                true,
                "complex mixed selector",
            ),
            ("html > body > section > div", true, "all direct children"),
            ("html > body > div", false, "broken chain (missing section)"),
            ("section > p", false, "wrong child element"),
            ("div.inactive", false, "wrong class"),
        ];

        for (selector_str, expected, description) in test_cases {
            let selector = parse_selector(selector_str).unwrap();
            let result = matches_prefix(&stack, &selector);
            assert_eq!(
                result, expected,
                "{description}: '{selector_str}' should be {expected}"
            );
        }
    }

    #[test]
    fn test_builder_pattern_equivalence() {
        // Test that manual builder pattern produces same results as parsing
        let element = StackFrame {
            metaline: MetaLine::parse(".div .. .container .active data-enabled=true :main", 0)
                .unwrap(),
            child_index: 2,
        };
        let stack = vec![element];

        // Manual construction
        let manual_selector = SimpleSelectorGroup {
            selectors: vec![SimpleSelector::builder()
                .tag("div")
                .class("container")
                .class("active")
                .id("main")
                .attribute("data-enabled")
                .nth_child(3) // child_index 2 = 3rd child
                .part()],
        };

        // Parsed construction (note: nth-child is positional, not parsed from string in this context)
        let parsed_selector = parse_selector("div.container.active#main[data-enabled]").unwrap();

        let result_manual = matches_prefix(&stack, &manual_selector);
        let result_parsed = matches_prefix(&stack, &parsed_selector);

        // Both should match the element (manual has nth-child, parsed doesn't, but element still matches)
        assert!(result_manual, "Manual selector should match");
        assert!(result_parsed, "Parsed selector should match");
    }

    #[test]
    fn test_multiple_not_conditions() {
        let element = StackFrame {
            metaline: MetaLine::parse(".div .. .widget .enabled", 0).unwrap(),
            child_index: 0,
        };
        let stack = vec![element];

        // Should match: has .widget, doesn't have .disabled or .hidden
        let selector = SimpleSelector::builder()
            .tag("div")
            .class("widget")
            .not(SimpleSelector::builder().class("disabled").build())
            .not(SimpleSelector::builder().class("hidden").build())
            .build();
        let selector_group = SimpleSelectorGroup {
            selectors: vec![SelectorPart::Simple(Box::new(selector))],
        };

        assert!(
            matches_prefix(&stack, &selector_group),
            "Should match with multiple :not conditions"
        );

        // Should not match: has .enabled, so :not(.enabled) fails
        let selector_fail = SimpleSelector::builder()
            .tag("div")
            .not(SimpleSelector::builder().class("enabled").build())
            .build();
        let selector_group_fail = SimpleSelectorGroup {
            selectors: vec![SelectorPart::Simple(Box::new(selector_fail))],
        };

        assert!(
            !matches_prefix(&stack, &selector_group_fail),
            "Should not match when :not condition is present"
        );
    }

    #[test]
    fn test_edge_case_metaline_parsing() {
        // Test edge cases in MetaLine parsing that might affect selector matching

        // Empty class list
        let element = StackFrame {
            metaline: MetaLine::parse(".div", 0).unwrap(),
            child_index: 0,
        };
        let stack = vec![element];

        let selector = parse_selector("div").unwrap();
        assert!(
            matches_prefix(&stack, &selector),
            "Should match element with no classes"
        );

        let selector = parse_selector("div.missing").unwrap();
        assert!(
            !matches_prefix(&stack, &selector),
            "Should not match missing class"
        );

        // Test class name with spaces (note: this may not be supported by the current parser)
        let element = StackFrame {
            metaline: MetaLine::parse(".div .. .regular-class", 0).unwrap(),
            child_index: 0,
        };
        let stack = vec![element];

        let selector = parse_selector("div.regular-class").unwrap();
        assert!(
            matches_prefix(&stack, &selector),
            "Should match regular class name"
        );
    }

    #[test]
    fn test_performance_with_deep_stacks() {
        // Create a deep stack to test performance doesn't degrade significantly
        let mut stack = vec![];
        for i in 0..50 {
            stack.push(StackFrame {
                metaline: MetaLine::parse(&format!(".div .. .level{i}"), 0).unwrap(),
                child_index: i % 3,
            });
        }

        // Test various selectors on deep stack
        let test_selectors = vec![
            "div",             // Should match (multiple elements)
            "div.level0",      // Should match first element
            "div.level49",     // Should match last element
            "div.level25",     // Should match middle element
            "div.nonexistent", // Should not match
        ];

        for selector_str in test_selectors {
            let selector = parse_selector(selector_str).unwrap();
            let _result = matches_prefix(&stack, &selector);
            // Just ensure it completes without hanging or errors
        }
    }
}
