use ocg::graph::PropertyGraph;
use ocg::execute;

#[test]
fn test_db_labels() {
    let mut graph = PropertyGraph::new();

    // Create some nodes with different labels using Cypher
    execute(&mut graph, "CREATE (:Person:Employee), (:Person), (:Company)").unwrap();

    let query = "CALL db.labels() YIELD label RETURN label";
    let result = execute(&mut graph, query).expect("Failed to execute");

    // Should return 3 unique labels: Company, Employee, Person (sorted)
    assert_eq!(result.rows.len(), 3);

    let labels: Vec<String> = result.rows.iter()
        .map(|row| row.get("label").and_then(|v| v.as_string()).unwrap_or("").to_string())
        .collect();

    assert_eq!(labels, vec!["Company", "Employee", "Person"]);
}

#[test]
fn test_db_relationship_types() {
    let mut graph = PropertyGraph::new();

    // Create some relationships with different types using Cypher
    execute(&mut graph, "CREATE (a)-[:KNOWS]->(b)-[:KNOWS]->(c), (a)-[:WORKS_FOR]->(c)").unwrap();

    let query = "CALL db.relationshipTypes() YIELD relationshipType RETURN relationshipType";
    let result = execute(&mut graph, query).expect("Failed to execute");

    // Should return 2 unique types: KNOWS, WORKS_FOR (sorted)
    assert_eq!(result.rows.len(), 2);

    let types: Vec<String> = result.rows.iter()
        .map(|row| row.get("relationshipType").and_then(|v| v.as_string()).unwrap_or("").to_string())
        .collect();

    assert_eq!(types, vec!["KNOWS", "WORKS_FOR"]);
}

#[test]
fn test_db_property_keys() {
    let mut graph = PropertyGraph::new();

    // Create nodes and relationships with properties using Cypher
    execute(&mut graph, r#"
        CREATE (a {name: 'Alice', age: 30})-[:KNOWS {since: 2020}]->(b {name: 'Bob', email: 'bob@example.com'})
    "#).unwrap();

    let query = "CALL db.propertyKeys() YIELD propertyKey RETURN propertyKey";
    let result = execute(&mut graph, query).expect("Failed to execute");

    // Should return unique property keys (sorted)
    let keys: Vec<String> = result.rows.iter()
        .map(|row| row.get("propertyKey").and_then(|v| v.as_string()).unwrap_or("").to_string())
        .collect();

    assert!(keys.contains(&"name".to_string()));
    assert!(keys.contains(&"age".to_string()));
    assert!(keys.contains(&"email".to_string()));
    assert!(keys.contains(&"since".to_string()));
}

#[test]
fn test_dbms_procedures() {
    let mut graph = PropertyGraph::new();

    let query = "CALL dbms.procedures() YIELD name, description RETURN name, description";
    let result = execute(&mut graph, query).expect("Failed to execute");

    // Should return at least 4 procedures (db.labels, db.relationshipTypes, db.propertyKeys, dbms.procedures)
    assert!(result.rows.len() >= 4);

    let names: Vec<String> = result.rows.iter()
        .map(|row| row.get("name").and_then(|v| v.as_string()).unwrap_or("").to_string())
        .collect();

    assert!(names.contains(&"db.labels".to_string()));
    assert!(names.contains(&"db.relationshipTypes".to_string()));
    assert!(names.contains(&"db.propertyKeys".to_string()));
    assert!(names.contains(&"dbms.procedures".to_string()));
}

#[test]
fn test_call_with_yield_filter() {
    let mut graph = PropertyGraph::new();

    // Create nodes with different labels using Cypher
    execute(&mut graph, "CREATE (:Person), (:Company), (:Product)").unwrap();

    // Test YIELD with WHERE clause
    let query = "CALL db.labels() YIELD label WHERE label STARTS WITH 'P' RETURN label";
    let result = execute(&mut graph, query).expect("Failed to execute");

    // Should return only labels starting with 'P'
    assert_eq!(result.rows.len(), 2);

    let labels: Vec<String> = result.rows.iter()
        .map(|row| row.get("label").and_then(|v| v.as_string()).unwrap_or("").to_string())
        .collect();

    assert!(labels.contains(&"Person".to_string()));
    assert!(labels.contains(&"Product".to_string()));
}

#[test]
fn test_call_with_match() {
    let mut graph = PropertyGraph::new();

    // Create a person node using Cypher
    execute(&mut graph, "CREATE (:Person {name: 'Alice'})").unwrap();

    // Test MATCH followed by CALL
    let query = r#"
        MATCH (p:Person)
        CALL db.labels() YIELD label
        RETURN p.name AS person, label
        ORDER BY label
    "#;
    let result = execute(&mut graph, query).expect("Failed to execute");

    // Should return one row for the Person label
    assert_eq!(result.rows.len(), 1);

    let person = result.rows[0].get("person").and_then(|v| v.as_string());
    assert_eq!(person, Some("Alice"));

    let label = result.rows[0].get("label").and_then(|v| v.as_string());
    assert_eq!(label, Some("Person"));
}
