//! Interval and timestamp arithmetic operators

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters};
use crate::operator::Operator;
use crate::types::matcher::{ExactMatcher, NumericMatcher};
use crate::types::{Type, CALENDAR_INTERVAL, INT, INTERVAL, TIMESTAMP};

// ============================================================================
// Interval * Numeric
// ============================================================================

/// Interval * Numeric multiplication.
#[derive(Default, Clone)]
pub struct IntervalMultiplyNumeric;

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

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

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

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

/// Numeric * Interval multiplication.
#[derive(Default, Clone)]
pub struct NumericMultiplyInterval;

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

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

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

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

/// Interval / Numeric division.
#[derive(Default, Clone)]
pub struct IntervalDivideNumeric;

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

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

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

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

// ============================================================================
// CalendarInterval arithmetic
// ============================================================================

/// CalendarInterval + CalendarInterval addition.
#[derive(Default, Clone)]
pub struct CalendarIntervalPlusCalendarInterval;

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

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

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

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

/// CalendarInterval - CalendarInterval subtraction.
#[derive(Default, Clone)]
pub struct CalendarIntervalMinusCalendarInterval;

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

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

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

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

/// CalendarInterval * Int multiplication.
/// Note: Calendar intervals only support integer arithmetic (not floating-point).
#[derive(Default, Clone)]
pub struct CalendarIntervalMultiplyInt;

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

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

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

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

/// Int * CalendarInterval multiplication.
#[derive(Default, Clone)]
pub struct IntMultiplyCalendarInterval;

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

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

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

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

/// CalendarInterval / Int division.
#[derive(Default, Clone)]
pub struct CalendarIntervalDivideInt;

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

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

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

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

// ============================================================================
// Timestamp arithmetic
// ============================================================================

/// Timestamp - Interval subtraction.
#[derive(Default, Clone)]
pub struct TimestampMinusInterval;

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

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

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

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

/// Timestamp - CalendarInterval subtraction.
#[derive(Default, Clone)]
pub struct TimestampMinusCalendarInterval;

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

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

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

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

/// Interval + Timestamp addition.
#[derive(Default, Clone)]
pub struct IntervalPlusTimestamp;

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

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

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

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

/// CalendarInterval + Timestamp addition.
#[derive(Default, Clone)]
pub struct CalendarIntervalPlusTimestamp;

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

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

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

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

/// Timestamp - Timestamp subtraction (yields Interval).
#[derive(Default, Clone)]
pub struct TimestampMinusTimestamp;

impl FunctionDef for TimestampMinusTimestamp {
    fn name(&self) -> &'static str {
        Operator::Minus.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(INTERVAL)
    }

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

/// Timestamp + Interval addition.
#[derive(Default, Clone)]
pub struct TimestampPlusInterval;

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

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

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

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

/// Timestamp + CalendarInterval addition.
#[derive(Default, Clone)]
pub struct TimestampPlusCalendarInterval;

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

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

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

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

// ============================================================================
// Interval + Interval, Interval - Interval
// ============================================================================

/// Interval + Interval addition.
#[derive(Default, Clone)]
pub struct IntervalPlusInterval;

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

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

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

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

/// Interval - Interval subtraction.
#[derive(Default, Clone)]
pub struct IntervalMinusInterval;

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

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

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

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