//! Comparison operators: equality, relational, and null checking

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters};
use crate::func::utils::must_be_same_type;
use crate::operator::Operator;
use crate::types::matcher::{AnyMatcher, ExactMatcher, IntervalMatcher, NumericMatcher};
use crate::types::{Type, BOOLEAN, STRING, TIMESTAMP, UNKNOWN};

// =============================================================================
// Timestamp comparisons
// =============================================================================

#[derive(Default, Clone)]
pub struct TimestampEq;

impl FunctionDef for TimestampEq {
    fn name(&self) -> &'static str {
        Operator::Eq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(TIMESTAMP))
            .with("right", ExactMatcher::of(TIMESTAMP))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct TimestampNeq;

impl FunctionDef for TimestampNeq {
    fn name(&self) -> &'static str {
        Operator::Neq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(TIMESTAMP))
            .with("right", ExactMatcher::of(TIMESTAMP))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct TimestampLt;

impl FunctionDef for TimestampLt {
    fn name(&self) -> &'static str {
        Operator::Lt.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(TIMESTAMP))
            .with("right", ExactMatcher::of(TIMESTAMP))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct TimestampLte;

impl FunctionDef for TimestampLte {
    fn name(&self) -> &'static str {
        Operator::Lte.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(TIMESTAMP))
            .with("right", ExactMatcher::of(TIMESTAMP))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct TimestampGt;

impl FunctionDef for TimestampGt {
    fn name(&self) -> &'static str {
        Operator::Gt.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(TIMESTAMP))
            .with("right", ExactMatcher::of(TIMESTAMP))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct TimestampGte;

impl FunctionDef for TimestampGte {
    fn name(&self) -> &'static str {
        Operator::Gte.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(TIMESTAMP))
            .with("right", ExactMatcher::of(TIMESTAMP))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

// =============================================================================
// Interval comparisons
// =============================================================================

#[derive(Default, Clone)]
pub struct IntervalEq;

impl FunctionDef for IntervalEq {
    fn name(&self) -> &'static str {
        Operator::Eq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", IntervalMatcher::default())
            .with("right", IntervalMatcher::default())
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        // Ensure both are the same interval type
        must_be_same_type([
            bindings.get_by_index(0)?.typ(),
            bindings.get_by_index(1)?.typ(),
        ])?;
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct IntervalNeq;

impl FunctionDef for IntervalNeq {
    fn name(&self) -> &'static str {
        Operator::Neq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", IntervalMatcher::default())
            .with("right", IntervalMatcher::default())
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        must_be_same_type([
            bindings.get_by_index(0)?.typ(),
            bindings.get_by_index(1)?.typ(),
        ])?;
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct IntervalLt;

impl FunctionDef for IntervalLt {
    fn name(&self) -> &'static str {
        Operator::Lt.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", IntervalMatcher::default())
            .with("right", IntervalMatcher::default())
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        must_be_same_type([
            bindings.get_by_index(0)?.typ(),
            bindings.get_by_index(1)?.typ(),
        ])?;
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct IntervalLte;

impl FunctionDef for IntervalLte {
    fn name(&self) -> &'static str {
        Operator::Lte.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", IntervalMatcher::default())
            .with("right", IntervalMatcher::default())
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        must_be_same_type([
            bindings.get_by_index(0)?.typ(),
            bindings.get_by_index(1)?.typ(),
        ])?;
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct IntervalGt;

impl FunctionDef for IntervalGt {
    fn name(&self) -> &'static str {
        Operator::Gt.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", IntervalMatcher::default())
            .with("right", IntervalMatcher::default())
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        must_be_same_type([
            bindings.get_by_index(0)?.typ(),
            bindings.get_by_index(1)?.typ(),
        ])?;
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct IntervalGte;

impl FunctionDef for IntervalGte {
    fn name(&self) -> &'static str {
        Operator::Gte.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", IntervalMatcher::default())
            .with("right", IntervalMatcher::default())
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        must_be_same_type([
            bindings.get_by_index(0)?.typ(),
            bindings.get_by_index(1)?.typ(),
        ])?;
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

// =============================================================================
// Boolean comparisons
// =============================================================================

#[derive(Default, Clone)]
pub struct BooleanEq;

impl FunctionDef for BooleanEq {
    fn name(&self) -> &'static str {
        Operator::Eq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(BOOLEAN))
            .with("right", ExactMatcher::of(BOOLEAN))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct BooleanNeq;

impl FunctionDef for BooleanNeq {
    fn name(&self) -> &'static str {
        Operator::Neq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(BOOLEAN))
            .with("right", ExactMatcher::of(BOOLEAN))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

// =============================================================================
// String comparisons
// =============================================================================

#[derive(Default, Clone)]
pub struct StringEq;

impl FunctionDef for StringEq {
    fn name(&self) -> &'static str {
        Operator::Eq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(STRING))
            .with("right", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct StringNeq;

impl FunctionDef for StringNeq {
    fn name(&self) -> &'static str {
        Operator::Neq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", ExactMatcher::of(STRING))
            .with("right", ExactMatcher::of(STRING))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

// =============================================================================
// Numeric comparisons
// =============================================================================

#[derive(Default, Clone)]
pub struct NumericEq;

impl FunctionDef for NumericEq {
    fn name(&self) -> &'static str {
        Operator::Eq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", NumericMatcher::default())
            .with("right", NumericMatcher::default())
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct NumericNeq;

impl FunctionDef for NumericNeq {
    fn name(&self) -> &'static str {
        Operator::Neq.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", NumericMatcher::default())
            .with("right", NumericMatcher::default())
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct NumericLt;

impl FunctionDef for NumericLt {
    fn name(&self) -> &'static str {
        Operator::Lt.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", NumericMatcher::default())
            .with("right", NumericMatcher::default())
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct NumericLte;

impl FunctionDef for NumericLte {
    fn name(&self) -> &'static str {
        Operator::Lte.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", NumericMatcher::default())
            .with("right", NumericMatcher::default())
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct NumericGt;

impl FunctionDef for NumericGt {
    fn name(&self) -> &'static str {
        Operator::Gt.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", NumericMatcher::default())
            .with("right", NumericMatcher::default())
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct NumericGte;

impl FunctionDef for NumericGte {
    fn name(&self) -> &'static str {
        Operator::Gte.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", NumericMatcher::default())
            .with("right", NumericMatcher::default())
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

// =============================================================================
// IS / IS NOT (null checking)
// =============================================================================

#[derive(Default, Clone)]
pub struct Is;

impl FunctionDef for Is {
    fn name(&self) -> &'static str {
        Operator::Is.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", AnyMatcher::default())
            .with("right", ExactMatcher::of(UNKNOWN))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}

#[derive(Default, Clone)]
pub struct IsNot;

impl FunctionDef for IsNot {
    fn name(&self) -> &'static str {
        Operator::IsNot.str()
    }

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("left", AnyMatcher::default())
            .with("right", ExactMatcher::of(UNKNOWN))
    }

    fn return_type(&self, _bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(BOOLEAN)
    }

    fn type_id(&self) -> TypeId {
        TypeId::of::<Self>()
    }
}
