//! Eval implementations for comparison operators

use anyhow::bail;

use hamelin_lib::func::defs::{
    BooleanEq, BooleanNeq, IntervalEq, IntervalGt, IntervalGte, IntervalLt, IntervalLte,
    IntervalNeq, Is, IsNot, NumericEq, NumericGt, NumericGte, NumericLt, NumericLte, NumericNeq,
    StringEq, StringNeq, TimestampEq, TimestampGt, TimestampGte, TimestampLt, TimestampLte,
    TimestampNeq,
};

use crate::null_propagate;
use crate::registry::EvalRegistry;
use crate::value::{DecimalValue, Value};

/// Register all comparison operator eval implementations.
pub fn register(registry: &mut EvalRegistry) {
    // Timestamp comparisons
    registry.register_eval::<TimestampEq>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_timestamp()?
                == null_propagate!(params.take()?).require_timestamp()?,
        ))
    });

    registry.register_eval::<TimestampNeq>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_timestamp()?
                != null_propagate!(params.take()?).require_timestamp()?,
        ))
    });

    registry.register_eval::<TimestampLt>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_timestamp()?
                < null_propagate!(params.take()?).require_timestamp()?,
        ))
    });

    registry.register_eval::<TimestampLte>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_timestamp()?
                <= null_propagate!(params.take()?).require_timestamp()?,
        ))
    });

    registry.register_eval::<TimestampGt>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_timestamp()?
                > null_propagate!(params.take()?).require_timestamp()?,
        ))
    });

    registry.register_eval::<TimestampGte>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_timestamp()?
                >= null_propagate!(params.take()?).require_timestamp()?,
        ))
    });

    // Interval comparisons
    registry.register_eval::<IntervalEq>(|mut params| {
        let left = null_propagate!(params.take()?).require_interval()?;
        let right = null_propagate!(params.take()?).require_interval()?;
        Ok(Value::Boolean(left == right))
    });

    registry.register_eval::<IntervalNeq>(|mut params| {
        let left = null_propagate!(params.take()?).require_interval()?;
        let right = null_propagate!(params.take()?).require_interval()?;
        Ok(Value::Boolean(left != right))
    });

    registry.register_eval::<IntervalLt>(|mut params| {
        let left = null_propagate!(params.take()?).require_interval()?;
        let right = null_propagate!(params.take()?).require_interval()?;
        Ok(Value::Boolean(left < right))
    });

    registry.register_eval::<IntervalLte>(|mut params| {
        let left = null_propagate!(params.take()?).require_interval()?;
        let right = null_propagate!(params.take()?).require_interval()?;
        Ok(Value::Boolean(left <= right))
    });

    registry.register_eval::<IntervalGt>(|mut params| {
        let left = null_propagate!(params.take()?).require_interval()?;
        let right = null_propagate!(params.take()?).require_interval()?;
        Ok(Value::Boolean(left > right))
    });

    registry.register_eval::<IntervalGte>(|mut params| {
        let left = null_propagate!(params.take()?).require_interval()?;
        let right = null_propagate!(params.take()?).require_interval()?;
        Ok(Value::Boolean(left >= right))
    });

    // Boolean comparisons
    registry.register_eval::<BooleanEq>(|mut params| {
        let left = params.take()?;
        let right = params.take()?;
        match (left, right) {
            (Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l == r)),
            (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
            _ => bail!("Invalid operands for boolean operation"),
        }
    });

    registry.register_eval::<BooleanNeq>(|mut params| {
        let left = params.take()?;
        let right = params.take()?;
        match (left, right) {
            (Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l != r)),
            (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
            _ => bail!("Invalid operands for boolean operation"),
        }
    });

    // String comparisons
    registry.register_eval::<StringEq>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_string()?
                == null_propagate!(params.take()?).require_string()?,
        ))
    });

    registry.register_eval::<StringNeq>(|mut params| {
        Ok(Value::Boolean(
            null_propagate!(params.take()?).require_string()?
                != null_propagate!(params.take()?).require_string()?,
        ))
    });

    // Numeric comparisons
    registry.register_eval::<NumericEq>(|mut params| {
        let left = null_propagate!(params.take()?);
        let right = null_propagate!(params.take()?);
        let result = match (left, right) {
            (Value::Int(l), Value::Int(r)) => l == r,
            (Value::Double(l), Value::Double(r)) => l == r,
            (Value::Int(l), Value::Double(r)) => l as f64 == r,
            (Value::Double(l), Value::Int(r)) => l == r as f64,
            (Value::Decimal(l), Value::Decimal(r)) => l.eq(&r),
            (Value::Decimal(l), Value::Int(r)) => l.eq(&r.into()),
            (Value::Int(l), Value::Decimal(r)) => DecimalValue::from(l).eq(&r),
            (Value::Decimal(l), Value::Double(r)) => DecimalValue::from(r).eq(&l),
            (Value::Double(l), Value::Decimal(r)) => DecimalValue::from(l).eq(&r),
            _ => bail!("Invalid operands for numeric comparison"),
        };
        Ok(Value::Boolean(result))
    });

    registry.register_eval::<NumericNeq>(|mut params| {
        let left = null_propagate!(params.take()?);
        let right = null_propagate!(params.take()?);
        let result = match (left, right) {
            (Value::Int(l), Value::Int(r)) => l != r,
            (Value::Double(l), Value::Double(r)) => l != r,
            (Value::Int(l), Value::Double(r)) => l as f64 != r,
            (Value::Double(l), Value::Int(r)) => l != r as f64,
            (Value::Decimal(l), Value::Decimal(r)) => !l.eq(&r),
            (Value::Decimal(l), Value::Int(r)) => !l.eq(&r.into()),
            (Value::Int(l), Value::Decimal(r)) => !DecimalValue::from(l).eq(&r),
            (Value::Decimal(l), Value::Double(r)) => !DecimalValue::from(r).eq(&l),
            (Value::Double(l), Value::Decimal(r)) => !DecimalValue::from(l).eq(&r),
            _ => bail!("Invalid operands for numeric comparison"),
        };
        Ok(Value::Boolean(result))
    });

    registry.register_eval::<NumericLt>(|mut params| {
        let left = null_propagate!(params.take()?);
        let right = null_propagate!(params.take()?);
        let result = left.lt(&right);
        Ok(Value::Boolean(result))
    });

    registry.register_eval::<NumericLte>(|mut params| {
        let left = null_propagate!(params.take()?);
        let right = null_propagate!(params.take()?);
        let result = left.lte(&right);
        Ok(Value::Boolean(result))
    });

    registry.register_eval::<NumericGt>(|mut params| {
        let left = null_propagate!(params.take()?);
        let right = null_propagate!(params.take()?);
        let result = left.gt(&right);
        Ok(Value::Boolean(result))
    });

    registry.register_eval::<NumericGte>(|mut params| {
        let left = null_propagate!(params.take()?);
        let right = null_propagate!(params.take()?);
        let result = left.gte(&right);
        Ok(Value::Boolean(result))
    });

    // IS / IS NOT (null checking)
    registry.register_eval::<Is>(|mut params| {
        let left = params.take()?;
        let right = params.take()?;

        match right {
            Value::Null | Value::Unknown => {
                let is_null = matches!(left, Value::Null);
                Ok(Value::Boolean(is_null))
            }
            _ => bail!("IS operator can only be used with NULL"),
        }
    });

    registry.register_eval::<IsNot>(|mut params| {
        let left = params.take()?;
        let right = params.take()?;

        match right {
            Value::Null | Value::Unknown => {
                let is_null = matches!(left, Value::Null);
                Ok(Value::Boolean(!is_null))
            }
            _ => bail!("IS NOT operator can only be used with NULL"),
        }
    });

    // No reverse eval for comparison operators - they're not invertible
}
