//! OpenCypher parser module using pest.

use pest::Parser;
use pest_derive::Parser;

use crate::error::ParseError;

/// The Cypher parser, generated from the pest grammar.
#[derive(Parser)]
#[grammar = "parser/cypher.pest"]
pub struct CypherParser;

/// Parse a Cypher query string.
///
/// # Arguments
/// * `query` - The Cypher query string to parse
///
/// # Returns
/// * `Ok(Pairs)` - The parsed syntax tree
/// * `Err(ParseError)` - If parsing fails
pub fn parse(query: &str) -> Result<pest::iterators::Pairs<'_, Rule>, ParseError> {
    Ok(CypherParser::parse(Rule::script, query)?)
}

/// Validate a Cypher query without returning the parse tree.
///
/// # Arguments
/// * `query` - The Cypher query string to validate
///
/// # Returns
/// * `true` if the query is valid
/// * `false` if parsing fails
pub fn validate(query: &str) -> bool {
    parse(query).is_ok()
}

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

    #[test]
    fn test_simple_match() {
        assert!(validate("MATCH (n) RETURN n"));
    }

    #[test]
    fn test_match_with_label() {
        assert!(validate("MATCH (n:Person) RETURN n.name"));
    }

    #[test]
    fn test_match_with_relationship() {
        assert!(validate("MATCH (a)-[r:KNOWS]->(b) RETURN a, r, b"));
    }

    #[test]
    fn test_where_clause() {
        assert!(validate("MATCH (n) WHERE n.age > 21 RETURN n"));
    }

    #[test]
    fn test_limit_skip() {
        assert!(validate("MATCH (n) RETURN n SKIP 10 LIMIT 5"));
    }

    #[test]
    fn test_create() {
        assert!(validate("CREATE (n:Person {name: 'Alice'})"));
    }

    #[test]
    fn test_create_with_return() {
        assert!(validate("CREATE (n:Person {name: 'Alice'}) RETURN n"));
    }

    #[test]
    fn test_merge() {
        assert!(validate("MERGE (n:Person {name: 'Alice'}) RETURN n"));
    }

    #[test]
    fn test_merge_with_on_create() {
        assert!(validate("MERGE (n:Person {name: 'Alice'}) ON CREATE SET n.created = true RETURN n"));
    }

    #[test]
    fn test_delete() {
        assert!(validate("MATCH (n) DELETE n"));
    }

    #[test]
    fn test_detach_delete() {
        assert!(validate("MATCH (n) DETACH DELETE n"));
    }

    #[test]
    fn test_set() {
        assert!(validate("MATCH (n) SET n.name = 'Bob' RETURN n"));
    }

    #[test]
    fn test_remove() {
        assert!(validate("MATCH (n) REMOVE n.name RETURN n"));
    }

    #[test]
    fn test_with_clause() {
        assert!(validate("MATCH (n) WITH n.name AS name RETURN name"));
    }

    #[test]
    fn test_order_by() {
        assert!(validate("MATCH (n) RETURN n ORDER BY n.name"));
    }

    #[test]
    fn test_unwind() {
        assert!(validate("UNWIND [1, 2, 3] AS x RETURN x"));
    }

    #[test]
    fn test_case_expression() {
        assert!(validate("RETURN CASE WHEN true THEN 1 ELSE 0 END"));
    }

    #[test]
    fn test_case_expression_simple() {
        assert!(validate("MATCH (n) RETURN CASE n.type WHEN 'A' THEN 1 ELSE 0 END"));
    }

    #[test]
    fn test_list_comprehension() {
        assert!(validate("RETURN [x IN [1,2,3] WHERE x > 1 | x * 2]"));
    }

    #[test]
    fn test_parameter() {
        assert!(validate("MATCH (n) WHERE n.id = $id RETURN n"));
    }

    #[test]
    fn test_shortest_path() {
        assert!(validate("MATCH p = shortestPath((a)-[*]->(b)) RETURN p"));
    }

    #[test]
    fn test_all_shortest_paths() {
        assert!(validate("MATCH p = allShortestPaths((a)-[*]->(b)) RETURN p"));
    }

    #[test]
    fn test_union() {
        assert!(validate("MATCH (n:Person) RETURN n.name UNION MATCH (n:Company) RETURN n.name"));
    }

    #[test]
    fn test_union_all() {
        assert!(validate("MATCH (n:Person) RETURN n.name UNION ALL MATCH (n:Company) RETURN n.name"));
    }

    #[test]
    fn test_call() {
        assert!(validate("CALL db.labels()"));
    }

    #[test]
    fn test_call_with_yield() {
        assert!(validate("CALL db.labels() YIELD label RETURN label"));
    }

    #[test]
    fn test_foreach() {
        assert!(validate("MATCH (n) FOREACH (x IN [1,2,3] | SET n.prop = x)"));
    }

    #[test]
    fn test_string_predicates() {
        assert!(validate("MATCH (n) WHERE n.name STARTS WITH 'A' RETURN n"));
        assert!(validate("MATCH (n) WHERE n.name ENDS WITH 'z' RETURN n"));
        assert!(validate("MATCH (n) WHERE n.name CONTAINS 'test' RETURN n"));
    }

    #[test]
    fn test_null_predicates() {
        assert!(validate("MATCH (n) WHERE n.name IS NULL RETURN n"));
        assert!(validate("MATCH (n) WHERE n.name IS NOT NULL RETURN n"));
    }

    #[test]
    fn test_in_predicate() {
        assert!(validate("MATCH (n) WHERE n.age IN [1, 2, 3] RETURN n"));
    }

    #[test]
    fn test_regex_match() {
        assert!(validate("MATCH (n) WHERE n.name =~ '.*test.*' RETURN n"));
    }

    #[test]
    fn test_variable_length_relationship() {
        assert!(validate("MATCH (a)-[*1..3]->(b) RETURN a, b"));
        assert!(validate("MATCH (a)-[*..5]->(b) RETURN a, b"));
        assert!(validate("MATCH (a)-[*2..]->(b) RETURN a, b"));
        assert!(validate("MATCH (a)-[*]->(b) RETURN a, b"));
    }

    #[test]
    fn test_multiple_labels() {
        assert!(validate("MATCH (n:Person:Employee) RETURN n"));
    }

    #[test]
    fn test_map_literal() {
        assert!(validate("RETURN {name: 'Alice', age: 30}"));
    }

    #[test]
    fn test_list_literal() {
        assert!(validate("RETURN [1, 2, 3, 4, 5]"));
    }

    #[test]
    fn test_function_calls() {
        assert!(validate("RETURN count(*)"));
        assert!(validate("MATCH (n) RETURN count(n)"));
        assert!(validate("MATCH (n) RETURN collect(n.name)"));
        assert!(validate("RETURN abs(-5)"));
        assert!(validate("RETURN toUpper('hello')"));
    }

    #[test]
    fn test_aggregation_distinct() {
        assert!(validate("MATCH (n) RETURN count(DISTINCT n.name)"));
    }

    #[test]
    fn test_exists_subquery() {
        assert!(validate("MATCH (n) WHERE EXISTS { MATCH (n)-->(m) } RETURN n"));
    }

    #[test]
    fn test_reduce() {
        assert!(validate("RETURN reduce(s = 0, x IN [1,2,3] | s + x)"));
    }

    #[test]
    fn test_all_any_none_single() {
        assert!(validate("RETURN all(x IN [1,2,3] WHERE x > 0)"));
        assert!(validate("RETURN any(x IN [1,2,3] WHERE x > 2)"));
        assert!(validate("RETURN none(x IN [1,2,3] WHERE x > 5)"));
        assert!(validate("RETURN single(x IN [1,2,3] WHERE x = 2)"));
    }

    #[test]
    fn test_pattern_comprehension() {
        assert!(validate("MATCH (n) RETURN [(n)-->(m) | m.name]"));
    }

    #[test]
    fn test_invalid_query() {
        assert!(!validate("foo"));
        assert!(!validate("MATCH"));
        assert!(!validate("RETURN"));
    }
}
