use crate::eval::environment::Environment;
use crate::eval::evaluator::eval;
use crate::value::Value;
use hamelin_lib::tree::builder::{add, field_ref, is_not_null, is_null, null, ExpressionBuilder};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::type_check_expression;
use hamelin_lib::types::INT;
use std::sync::Arc;

use super::test_helpers::TestContext;

#[test]
fn test_eval_null_propagation() {
    let mut ctx = TestContext::default();
    ctx.set("x", Value::Null, INT);

    // Test: x + 5 should return null (null propagation)
    let expr = type_check_expression(
        add(field_ref("x"), 5).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();

    assert_eq!(result, Value::Null);
    assert!(result.is_null());
}

#[test]
fn test_eval_is_null_operators() {
    let env = Environment::new();

    // Test IS NULL with null value
    let expr = type_check_expression(
        is_null(null()).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test IS NOT NULL with non-null value
    let expr = type_check_expression(
        is_not_null(42).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test IS NULL with non-null value
    let expr =
        type_check_expression(is_null(42).build(), ExpressionTypeCheckOptions::default()).output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(false));

    // Test IS NOT NULL with null value
    let expr = type_check_expression(
        is_not_null(null()).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(false));
}

#[test]
fn test_eval_null_literal() {
    let ctx = TestContext::new();

    // Test null literal evaluation
    let expr = null();
    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::Null);
    assert!(result.is_null());
}

#[test]
fn test_null_propagation_in_arithmetic() {
    let mut ctx = TestContext::default();
    ctx.set("null_var", Value::Null, INT);
    ctx.set("null_var2", Value::Null, INT);

    // Test null + number = null
    let expr = type_check_expression(
        add(field_ref("null_var"), 42).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();
    assert_eq!(result, Value::Null);

    // Test number + null = null
    let expr = type_check_expression(
        add(42, field_ref("null_var")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();
    assert_eq!(result, Value::Null);

    // Test null + null = null
    let expr = type_check_expression(
        add(field_ref("null_var"), field_ref("null_var2")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();
    assert_eq!(result, Value::Null);
}

#[test]
fn test_null_propagation_in_comparisons() {
    use hamelin_lib::tree::builder::{eq, gt};

    let mut ctx = TestContext::default();
    ctx.set("null_var", Value::Null, INT);
    ctx.set("null_var2", Value::Null, INT);

    // Test null = number should return null (three-valued logic)
    let expr = type_check_expression(
        eq(field_ref("null_var"), 42).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();
    assert_eq!(result, Value::Null);

    // Test number = null should return null
    let expr = type_check_expression(
        eq(42, field_ref("null_var")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();
    assert_eq!(result, Value::Null);

    // Test null = null should return null
    let expr = type_check_expression(
        eq(field_ref("null_var"), field_ref("null_var2")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();
    assert_eq!(result, Value::Null);

    // Test null > number should return null
    let expr = type_check_expression(
        gt(field_ref("null_var"), 42).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();
    assert_eq!(result, Value::Null);
}
