use crate::eval::environment::Environment;
use crate::eval::evaluator::eval;
use crate::value::Value;
use hamelin_lib::tree::builder::{eq, gt, gte, lt, lte, ne, ExpressionBuilder};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::type_check_expression;

use super::test_helpers::setup_environment;

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

    // Test equality
    let expr =
        type_check_expression(eq(5, 5).build(), ExpressionTypeCheckOptions::default()).output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test inequality
    let expr =
        type_check_expression(ne(5, 3).build(), ExpressionTypeCheckOptions::default()).output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test less than
    let expr =
        type_check_expression(lt(3, 5).build(), ExpressionTypeCheckOptions::default()).output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test greater than or equal
    let expr =
        type_check_expression(gte(5, 5).build(), ExpressionTypeCheckOptions::default()).output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));
}

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

    // Test string equality
    let expr = type_check_expression(
        eq("hello", "hello").build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test string inequality
    let expr = type_check_expression(
        ne("hello", "world").build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));
}

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

    // Test int vs double comparison
    let expr =
        type_check_expression(eq(5, 5.0).build(), ExpressionTypeCheckOptions::default()).output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test int vs double less than
    let expr =
        type_check_expression(lt(3, 5.5).build(), ExpressionTypeCheckOptions::default()).output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));
}

#[test]
fn test_eval_timestamp_comparison() {
    use hamelin_lib::tree::builder::{call, string};

    let env = setup_environment();

    // Create two timestamps using the ts function
    let ts1_expr = type_check_expression(
        call("ts").arg(string("2023-01-01T00:00:00Z")).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let ts1_result = eval(&ts1_expr, &env).unwrap();

    let ts2_expr = type_check_expression(
        call("ts").arg(string("2023-01-02T00:00:00Z")).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let ts2_result = eval(&ts2_expr, &env).unwrap();

    // Extract timestamps from results
    let ts1 = match ts1_result {
        Value::Timestamp(ts) => ts,
        _ => panic!("Expected timestamp"),
    };

    let ts2 = match ts2_result {
        Value::Timestamp(ts) => ts,
        _ => panic!("Expected timestamp"),
    };

    // Test that ts1 < ts2
    assert!(ts1 < ts2);

    // Test comparison using lt expression
    let comparison_expr = type_check_expression(
        lt(
            call("ts").arg(string("2023-01-01T00:00:00Z")),
            call("ts").arg(string("2023-01-02T00:00:00Z")),
        )
        .build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let comparison_result = eval(&comparison_expr, &env).unwrap();
    assert_eq!(comparison_result, Value::Boolean(true));

    // Test equality
    let eq_expr = type_check_expression(
        eq(
            call("ts").arg(string("2023-01-01T00:00:00Z")),
            call("ts").arg(string("2023-01-01T00:00:00Z")),
        )
        .build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let eq_result = eval(&eq_expr, &env).unwrap();
    assert_eq!(eq_result, Value::Boolean(true));
}

#[test]
fn test_eval_decimal_comparison() {
    use hamelin_lib::tree::builder::decimal_from_parts;

    let env = setup_environment();

    // Test decimal equality: 3.14 == 3.14
    let expr = type_check_expression(
        eq(decimal_from_parts(314, 3, 2), decimal_from_parts(314, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test decimal inequality: 3.14 != 2.86
    let expr = type_check_expression(
        ne(decimal_from_parts(314, 3, 2), decimal_from_parts(286, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test decimal less than: 2.86 < 3.14
    let expr = type_check_expression(
        lt(decimal_from_parts(286, 3, 2), decimal_from_parts(314, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test decimal less than or equal: 3.14 <= 3.14
    let expr = type_check_expression(
        lte(decimal_from_parts(314, 3, 2), decimal_from_parts(314, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test decimal greater than: 3.14 > 2.86
    let expr = type_check_expression(
        gt(decimal_from_parts(314, 3, 2), decimal_from_parts(286, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test decimal greater than or equal: 3.14 >= 3.14
    let expr = type_check_expression(
        gte(decimal_from_parts(314, 3, 2), decimal_from_parts(314, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));
}

#[test]
fn test_eval_decimal_mixed_comparison() {
    use hamelin_lib::tree::builder::decimal_from_parts;

    let env = setup_environment();

    // Test decimal vs integer: 3.14 > 3
    let expr = type_check_expression(
        gt(decimal_from_parts(314, 3, 2), 3).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test integer vs decimal: 4 > 3.14
    let expr = type_check_expression(
        gt(4, decimal_from_parts(314, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test decimal vs double: 3.14 == 3.14 (as double)
    let expr = type_check_expression(
        eq(decimal_from_parts(314, 3, 2), 3.14).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));

    // Test double vs decimal: 3.15 > 3.14 (decimal)
    let expr = type_check_expression(
        gt(3.15, decimal_from_parts(314, 3, 2)).build(),
        ExpressionTypeCheckOptions::default(),
    )
    .output;
    let result = eval(&expr, &env).unwrap();
    assert_eq!(result, Value::Boolean(true));
}
