/// Comprehensive NULL handling audit for all functions
/// Tests that every function properly propagates NULL values

use std::collections::HashMap;
use ocg::{execute_with_params, CypherValue, PropertyGraph};

/// Helper to test a function with NULL in specified parameter position
fn test_null_handling(func_call: &str, expected_null: bool) -> bool {
    let mut graph = PropertyGraph::new();
    let params = HashMap::new();

    let query = format!("RETURN {} AS result", func_call);
    let result = execute_with_params(&mut graph, &query, params);

    match result {
        Ok(r) if r.rows.len() > 0 => {
            let val = r.rows[0].get("result");
            let is_null = matches!(val, Some(CypherValue::Null));
            if expected_null && !is_null {
                println!("❌ {} should return NULL, got: {:?}", func_call, val);
                return false;
            }
            true
        }
        Ok(_) => {
            println!("⚠️  {} returned empty result", func_call);
            false
        }
        Err(e) => {
            println!("❌ {} failed with error: {:?}", func_call, e);
            false
        }
    }
}

#[test]
fn test_math_functions_null_handling() {
    println!("\n=== Math Functions NULL Handling ===\n");

    let tests = vec![
        // Single argument functions
        ("abs(null)", true),
        ("ceil(null)", true),
        ("floor(null)", true),
        ("round(null)", true),
        ("sign(null)", true),
        ("sqrt(null)", true),
        ("exp(null)", true),
        ("log(null)", true),
        ("log10(null)", true),
        ("sin(null)", true),
        ("cos(null)", true),
        ("tan(null)", true),
        ("asin(null)", true),
        ("acos(null)", true),
        ("atan(null)", true),

        // Two argument functions
        ("atan2(null, 1.0)", true),
        ("atan2(1.0, null)", true),

        // No argument functions (should work)
        ("pi()", false),
        ("e()", false),
        ("rand()", false),
    ];

    let mut passed = 0;
    let mut failed = 0;

    for (test, expected_null) in tests {
        if test_null_handling(test, expected_null) {
            passed += 1;
        } else {
            failed += 1;
        }
    }

    println!("\n📊 Math Functions: {} passed, {} failed", passed, failed);
    assert_eq!(failed, 0, "Some math functions don't handle NULL correctly");
}

#[test]
fn test_string_functions_null_handling() {
    println!("\n=== String Functions NULL Handling ===\n");

    let tests = vec![
        // Single argument
        ("toString(null)", true),
        ("toUpper(null)", true),
        ("toLower(null)", true),
        ("trim(null)", true),
        ("ltrim(null)", true),
        ("rtrim(null)", true),
        ("reverse(null)", true),

        // Two arguments
        ("substring(null, 0)", true),
        ("substring('hello', null)", true),
        ("left(null, 5)", true),
        ("left('hello', null)", true),
        ("right(null, 5)", true),
        ("right('hello', null)", true),
        ("split(null, ',')", true),
        ("split('a,b', null)", true),
        ("startsWith(null, 'x')", true),
        ("startsWith('hello', null)", true),
        ("endsWith(null, 'x')", true),
        ("endsWith('hello', null)", true),
        ("contains(null, 'x')", true),
        ("contains('hello', null)", true),

        // Three arguments
        ("replace(null, 'a', 'b')", true),
        ("replace('hello', null, 'b')", true),
        ("replace('hello', 'a', null)", true),
        ("substring(null, 0, 5)", true),
        ("substring('hello', null, 5)", true),
        ("substring('hello', 0, null)", true),
    ];

    let mut passed = 0;
    let mut failed = 0;

    for (test, expected_null) in tests {
        if test_null_handling(test, expected_null) {
            passed += 1;
        } else {
            failed += 1;
        }
    }

    println!("\n📊 String Functions: {} passed, {} failed", passed, failed);
    assert_eq!(failed, 0, "Some string functions don't handle NULL correctly");
}

#[test]
fn test_list_functions_null_handling() {
    println!("\n=== List Functions NULL Handling ===\n");

    let tests = vec![
        ("size(null)", true),
        ("length(null)", true),
        ("head(null)", true),
        ("last(null)", true),
        ("tail(null)", true),
        ("reverse(null)", true),

        // With arguments
        ("range(null, 10)", true),
        ("range(0, null)", true),
        ("range(0, 10, null)", true),
    ];

    let mut passed = 0;
    let mut failed = 0;

    for (test, expected_null) in tests {
        if test_null_handling(test, expected_null) {
            passed += 1;
        } else {
            failed += 1;
        }
    }

    println!("\n📊 List Functions: {} passed, {} failed", passed, failed);
    assert_eq!(failed, 0, "Some list functions don't handle NULL correctly");
}

#[test]
fn test_type_conversion_null_handling() {
    println!("\n=== Type Conversion NULL Handling ===\n");

    let tests = vec![
        ("toBoolean(null)", true),
        ("toFloat(null)", true),
        ("toInteger(null)", true),
        ("toString(null)", true),
    ];

    let mut passed = 0;
    let mut failed = 0;

    for (test, expected_null) in tests {
        if test_null_handling(test, expected_null) {
            passed += 1;
        } else {
            failed += 1;
        }
    }

    println!("\n📊 Type Conversion: {} passed, {} failed", passed, failed);
    assert_eq!(failed, 0, "Some type conversion functions don't handle NULL correctly");
}

#[test]
fn test_temporal_functions_null_handling() {
    println!("\n=== Temporal Functions NULL Handling ===\n");

    let tests = vec![
        // These should handle NULL in map arguments
        ("date({year: null})", true),
        ("time({hour: null})", true),
        ("datetime({year: null})", true),
        ("localdatetime({year: null})", true),
        ("localtime({hour: null})", true),
        ("duration({days: null})", true),
    ];

    let mut passed = 0;
    let mut failed = 0;

    for (test, expected_null) in tests {
        if test_null_handling(test, expected_null) {
            passed += 1;
        } else {
            failed += 1;
        }
    }

    println!("\n📊 Temporal Functions: {} passed, {} failed", passed, failed);
    // Note: Some temporal NULL handling may be intentional behavior
}

#[test]
fn test_graph_functions_null_handling() {
    println!("\n=== Graph Functions NULL Handling ===\n");

    let tests = vec![
        ("id(null)", true),
        ("labels(null)", true),
        ("keys(null)", true),
        ("properties(null)", true),
        ("type(null)", true),
    ];

    let mut passed = 0;
    let mut failed = 0;

    for (test, expected_null) in tests {
        if test_null_handling(test, expected_null) {
            passed += 1;
        } else {
            failed += 1;
        }
    }

    println!("\n📊 Graph Functions: {} passed, {} failed", passed, failed);
}
