//! Tests for timestamp arithmetic reverse evaluation (timestamp +/- interval, timestamp - timestamp)

use crate::eval::environment::Environment;
use crate::reverse_eval::domain::Constraint;
use crate::reverse_eval::reverse::reverse_eval;
use crate::value::{TimestampValue, Value};
use chrono::{Duration, TimeZone, Utc};
use hamelin_lib::tree::builder::*;
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::tree::typed_ast::environment::TypeEnvironment;
use hamelin_lib::type_check_expression;
use hamelin_lib::types::{CALENDAR_INTERVAL, INT, INTERVAL, TIMESTAMP};
use pretty_assertions::assert_eq;
use rstest::rstest;
use std::sync::Arc;

// Helper function to create timestamp literals using the ts() function
fn ts(timestamp_str: &str) -> FunctionCallBuilder {
    call("ts").arg(StringLiteralBuilder::new(timestamp_str))
}

#[rstest]
#[case::timestamp_plus_interval(
    add(field_ref("ts"), days(1)),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 16, 0, 0, 0).unwrap()).into()),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 15, 0, 0, 0).unwrap()).into())
)]
#[case::timestamp_minus_interval(
    subtract(field_ref("ts"), hours(2)),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 15, 10, 0, 0).unwrap()).into()),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 15, 12, 0, 0).unwrap()).into())
)]
fn test_reverse_eval_timestamp_interval_arithmetic_equals(
    #[case] expr_builder: impl ExpressionBuilder,
    #[case] output_constraint: Constraint,
    #[case] expected_constraint: Constraint,
) {
    let env = Environment::new();

    // Setup translation environment with bindings for ts and iv
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ts", TIMESTAMP);
    trans_env.bind_str("iv", INTERVAL);
    let bindings = Arc::new(trans_env);

    let expr = type_check_expression(
        expr_builder.build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(expected_constraint));
}

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

    // Setup translation environment with bindings
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ts", TIMESTAMP);
    let bindings = Arc::new(trans_env);

    // Build: ts + INTERVAL '1' HOUR
    let expr = type_check_expression(
        add(field_ref("ts"), hours(1)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // If ts + 1hour in [2024-01-15 14:00, 2024-01-15 16:00],
    // then ts should be in [2024-01-15 13:00, 2024-01-15 15:00]
    let min_output = Utc.with_ymd_and_hms(2024, 1, 15, 14, 0, 0).unwrap();
    let max_output = Utc.with_ymd_and_hms(2024, 1, 15, 16, 0, 0).unwrap();
    let constraint = Constraint::Range {
        min: Some(TimestampValue::utc(min_output).into()),
        max: Some(TimestampValue::utc(max_output).into()),
    };
    let result = reverse_eval(&expr, constraint, &env).expect("Reverse eval failed");

    let expected_min = Utc.with_ymd_and_hms(2024, 1, 15, 13, 0, 0).unwrap();
    let expected_max = Utc.with_ymd_and_hms(2024, 1, 15, 15, 0, 0).unwrap();
    assert_eq!(
        result,
        Some(Constraint::Range {
            min: Some(TimestampValue::utc(expected_min).into()),
            max: Some(TimestampValue::utc(expected_max).into()),
        })
    );
}

#[rstest]
#[case::interval_plus_timestamp(
    add(field_ref("iv"), ts("2024-01-15 00:00:00+00:00")),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 20, 0, 0, 0).unwrap()).into()),
    Constraint::Equals(Value::Interval(Duration::days(5)))
)]
#[case::timestamp_minus_timestamp(
    subtract(field_ref("ts"), ts("2024-01-10 00:00:00+00:00")),
    Constraint::Equals(Value::Interval(Duration::days(3))),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 13, 0, 0, 0).unwrap()).into())
)]
#[case::timestamp_constant_minus_variable(
    subtract(ts("2024-01-15 00:00:00+00:00"), field_ref("ts")),
    Constraint::Equals(Value::Interval(Duration::days(2))),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 13, 0, 0, 0).unwrap()).into())
)]
fn test_reverse_eval_timestamp_with_intervals(
    #[case] expr_builder: impl ExpressionBuilder,
    #[case] output_constraint: Constraint,
    #[case] expected_constraint: Constraint,
) {
    let env = Environment::new();

    // Setup translation environment with bindings for ts and iv
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ts", TIMESTAMP);
    trans_env.bind_str("iv", INTERVAL);
    let bindings = Arc::new(trans_env);

    let expr = type_check_expression(
        expr_builder.build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(expected_constraint));
}

// ============================================================================
// CalendarInterval + Timestamp Reverse Evaluation Tests
// ============================================================================

#[rstest]
#[case::timestamp_plus_calendar_interval_months(
    add(field_ref("ts"), months(2)),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 3, 15, 0, 0, 0).unwrap()).into()),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 15, 0, 0, 0).unwrap()).into())
)]
#[case::calendar_interval_plus_timestamp(
    add(months(6), field_ref("ts")),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 7, 1, 0, 0, 0).unwrap()).into()),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap()).into())
)]
#[case::timestamp_minus_calendar_interval(
    subtract(field_ref("ts"), years(1)),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2023, 6, 15, 0, 0, 0).unwrap()).into()),
    Constraint::Equals(TimestampValue::utc(Utc.with_ymd_and_hms(2024, 6, 15, 0, 0, 0).unwrap()).into())
)]
fn test_reverse_eval_calendar_interval_timestamp_arithmetic(
    #[case] expr_builder: impl ExpressionBuilder,
    #[case] output_constraint: Constraint,
    #[case] expected_constraint: Constraint,
) {
    let env = Environment::new();

    // Setup translation environment with bindings
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ts", TIMESTAMP);
    let bindings = Arc::new(trans_env);

    let expr = type_check_expression(
        expr_builder.build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(expected_constraint));
}

// ============================================================================
// Interval Arithmetic Reverse Evaluation Tests
// ============================================================================

#[rstest]
#[case::interval_plus_interval(
    add(field_ref("iv"), days(2)),
    Constraint::Equals(Value::Interval(Duration::days(5))),
    Constraint::Equals(Value::Interval(Duration::days(3)))
)]
#[case::interval_minus_interval(
    subtract(field_ref("iv"), hours(3)),
    Constraint::Equals(Value::Interval(Duration::hours(7))),
    Constraint::Equals(Value::Interval(Duration::hours(10)))
)]
#[case::interval_multiply_numeric(
    multiply(field_ref("iv"), int(3)),
    Constraint::Equals(Value::Interval(Duration::hours(9))),
    Constraint::Equals(Value::Interval(Duration::hours(3)))
)]
#[case::numeric_multiply_interval(
    multiply(int(2), field_ref("iv")),
    Constraint::Equals(Value::Interval(Duration::days(10))),
    Constraint::Equals(Value::Interval(Duration::days(5)))
)]
#[case::interval_divide_numeric(
    divide(field_ref("iv"), int(4)),
    Constraint::Equals(Value::Interval(Duration::hours(6))),
    Constraint::Equals(Value::Interval(Duration::hours(24)))
)]
fn test_reverse_eval_interval_arithmetic(
    #[case] expr_builder: impl ExpressionBuilder,
    #[case] output_constraint: Constraint,
    #[case] expected_constraint: Constraint,
) {
    let env = Environment::new();

    // Setup translation environment with bindings
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("iv", INTERVAL);
    let bindings = Arc::new(trans_env);

    let expr = type_check_expression(
        expr_builder.build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(expected_constraint));
}

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

    // Setup translation environment
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("iv", INTERVAL);
    let bindings = Arc::new(trans_env);

    // Build: iv + 2 days
    let expr = type_check_expression(
        add(field_ref("iv"), days(2)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // If iv + 2days in [5 days, 10 days], then iv should be in [3 days, 8 days]
    let min_output = Duration::days(5);
    let max_output = Duration::days(10);
    let constraint = Constraint::Range {
        min: Some(Value::Interval(min_output)),
        max: Some(Value::Interval(max_output)),
    };
    let result = reverse_eval(&expr, constraint, &env).expect("Reverse eval failed");

    let expected_min = Duration::days(3);
    let expected_max = Duration::days(8);
    assert_eq!(
        result,
        Some(Constraint::Range {
            min: Some(Value::Interval(expected_min)),
            max: Some(Value::Interval(expected_max)),
        })
    );
}

// ============================================================================
// CalendarInterval Arithmetic Reverse Evaluation Tests
// ============================================================================

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

    // Setup translation environment
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Build: ci + 6 months
    let expr = type_check_expression(
        add(field_ref("ci"), months(6)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // If ci + 6 months = 12 months, then ci = 6 months
    let output_constraint = Constraint::Equals(Value::CalendarInterval(12));
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(Constraint::Equals(Value::CalendarInterval(6))));
}

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

    // Setup translation environment
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Build: ci - 3 months
    let expr = type_check_expression(
        subtract(field_ref("ci"), months(3)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // If ci - 3 months = 9 months, then ci = 12 months
    let output_constraint = Constraint::Equals(Value::CalendarInterval(9));
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(
        result,
        Some(Constraint::Equals(Value::CalendarInterval(12)))
    );
}

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

    // Setup translation environment
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Build: ci * 3
    let expr = type_check_expression(
        multiply(field_ref("ci"), int(3)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // If ci * 3 = 12 months, then ci = 4 months
    let output_constraint = Constraint::Equals(Value::CalendarInterval(12));
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(result, Some(Constraint::Equals(Value::CalendarInterval(4))));
}

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

    // Setup translation environment
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Build: ci / 2
    let expr = type_check_expression(
        divide(field_ref("ci"), int(2)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;

    // If ci / 2 = 6 months, then ci = 12 months
    let output_constraint = Constraint::Equals(Value::CalendarInterval(6));
    let result = reverse_eval(&expr, output_constraint, &env).expect("Reverse eval failed");

    assert_eq!(
        result,
        Some(Constraint::Equals(Value::CalendarInterval(12)))
    );
}

// ============================================================================
// Error Case Tests
// ============================================================================

#[test]
fn test_reverse_interval_multiply_division_by_zero() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("x", INTERVAL);
    let bindings = Arc::new(trans_env);

    // Test: x * 0 = interval (division by zero when solving for x)
    let expr = type_check_expression(
        multiply(field_ref("x"), int(0)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(Value::Interval(Duration::hours(1)));
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail with division by zero error
    assert!(
        result.is_err(),
        "Expected error for division by zero in reverse multiplication"
    );
    let err_msg = result.unwrap_err().to_string();
    assert!(
        err_msg.contains("Division by zero"),
        "Expected 'Division by zero' error, got: {}",
        err_msg
    );
}

#[test]
fn test_reverse_calendar_interval_multiply_division_by_zero() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Test: ci * 0 = output (division by zero when solving for ci)
    let expr = type_check_expression(
        multiply(field_ref("ci"), int(0)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(Value::CalendarInterval(6));
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail with division by zero error
    assert!(
        result.is_err(),
        "Expected error for division by zero in reverse multiplication"
    );
    let err_msg = result.unwrap_err().to_string();
    assert!(
        err_msg.contains("Division by zero"),
        "Expected 'Division by zero' error, got: {}",
        err_msg
    );
}

#[test]
fn test_reverse_interval_division_with_zero_output() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("interval_col", INTERVAL);
    trans_env.bind_str("x", INT);
    let bindings = Arc::new(trans_env);

    // Test: interval / x = 0 interval (division by zero when solving for x)
    let expr = type_check_expression(
        divide(field_ref("interval_col"), field_ref("x")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(Value::Interval(Duration::zero()));
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail with division by zero error when trying to solve for x
    // because x = interval / output, and output is zero
    assert!(
        result.is_err(),
        "Expected error for division by zero in reverse division"
    );
}

#[test]
fn test_reverse_interval_multiply_with_zero_interval() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("x", INT);
    let bindings = Arc::new(trans_env);

    // Test: 0 interval * x = 1 hour (solving for x requires dividing by zero interval)
    let expr = type_check_expression(
        multiply(hours(0), field_ref("x")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(Value::Interval(Duration::hours(1)));
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail with division by zero error when trying to solve for x
    // because x = output / interval, and interval is zero
    assert!(
        result.is_err(),
        "Expected error for division by zero in reverse multiplication"
    );
    let err_msg = result.unwrap_err().to_string();
    assert!(
        err_msg.contains("Division by zero"),
        "Expected 'Division by zero' error, got: {}",
        err_msg
    );
}

#[test]
fn test_reverse_numeric_multiply_interval_with_zero_interval() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("x", INT);
    let bindings = Arc::new(trans_env);

    // Test: x * 0 interval = 1 hour (solving for x requires dividing by zero interval)
    let expr = type_check_expression(
        multiply(field_ref("x"), hours(0)).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(Value::Interval(Duration::hours(1)));
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail with division by zero error when trying to solve for x
    // because x = output / interval, and interval is zero
    assert!(
        result.is_err(),
        "Expected error for division by zero in reverse multiplication"
    );
    let err_msg = result.unwrap_err().to_string();
    assert!(
        err_msg.contains("Division by zero"),
        "Expected 'Division by zero' error, got: {}",
        err_msg
    );
}
// ============================================================================
// Calendar Interval from Timestamp Arithmetic Error Cases
// ============================================================================

#[test]
fn test_reverse_calendar_interval_plus_timestamp_unsolvable() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Test: ci + ts('2024-01-15 00:00:00+00:00') = ts('2024-06-15 00:00:00+00:00')
    // This attempts to solve for calendar interval: ci = output_ts - input_ts
    // But timestamp - timestamp yields fixed Interval, not CalendarInterval
    let expr = type_check_expression(
        add(field_ref("ci"), ts("2024-01-15 00:00:00+00:00")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(
        TimestampValue::utc(Utc.with_ymd_and_hms(2024, 6, 15, 0, 0, 0).unwrap()).into(),
    );
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail because we cannot derive a calendar interval from timestamp arithmetic
    assert!(
        result.is_err(),
        "Expected error when solving for calendar interval from timestamp arithmetic, but got: {:?}",
        result
    );
    let err_msg = result.unwrap_err().to_string();
    assert!(
        err_msg.contains("Cannot solve for calendar interval from timestamp arithmetic"),
        "Expected 'Cannot solve for calendar interval' error, got: {}",
        err_msg
    );
}

#[test]
fn test_reverse_timestamp_plus_calendar_interval_unsolvable() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Test: ts('2024-01-01 00:00:00+00:00') + ci = ts('2024-07-01 00:00:00+00:00')
    // This attempts to solve for calendar interval: ci = output_ts - input_ts
    // But timestamp - timestamp yields fixed Interval, not CalendarInterval
    let expr = type_check_expression(
        add(ts("2024-01-01 00:00:00+00:00"), field_ref("ci")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(
        TimestampValue::utc(Utc.with_ymd_and_hms(2024, 7, 1, 0, 0, 0).unwrap()).into(),
    );
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail because we cannot derive a calendar interval from timestamp arithmetic
    assert!(
        result.is_err(),
        "Expected error when solving for calendar interval from timestamp arithmetic, but got: {:?}",
        result
    );
    let err_msg = result.unwrap_err().to_string();
    assert!(
        err_msg.contains("Cannot solve for calendar interval from timestamp arithmetic"),
        "Expected 'Cannot solve for calendar interval' error, got: {}",
        err_msg
    );
}

#[test]
fn test_reverse_timestamp_minus_calendar_interval_unsolvable() {
    let env = Environment::new();
    let mut trans_env = TypeEnvironment::default();
    trans_env.bind_str("ci", CALENDAR_INTERVAL);
    let bindings = Arc::new(trans_env);

    // Test: ts('2024-06-15 00:00:00+00:00') - ci = ts('2024-01-15 00:00:00+00:00')
    // This attempts to solve for calendar interval: ci = input_ts - output_ts
    // But timestamp - timestamp yields fixed Interval, not CalendarInterval
    let expr = type_check_expression(
        subtract(ts("2024-06-15 00:00:00+00:00"), field_ref("ci")).build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(bindings)
            .build(),
    )
    .output;
    let output_constraint = Constraint::Equals(
        TimestampValue::utc(Utc.with_ymd_and_hms(2024, 1, 15, 0, 0, 0).unwrap()).into(),
    );
    let result = reverse_eval(&expr, output_constraint, &env);

    // Should fail because we cannot derive a calendar interval from timestamp arithmetic
    assert!(
        result.is_err(),
        "Expected error when solving for calendar interval from timestamp arithmetic"
    );
    let err_msg = result.unwrap_err().to_string();
    assert!(
        err_msg.contains("Cannot solve for calendar interval from timestamp arithmetic"),
        "Expected 'Cannot solve for calendar interval' error, got: {}",
        err_msg
    );
}
