//! Unary operators: UnaryMinus, UnaryPlus, UnaryRangePrefix, UnaryRangePostfix

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters};
use crate::operator::Operator;
use crate::types::matcher::{ExactMatcher, IntervalMatcher, NumericMatcher, OrMatcher};
use crate::types::range::Range;
use crate::types::{Type, TIMESTAMP};

/// Unary minus operator: `-x` for numeric and interval types
#[derive(Default, Clone)]
pub struct UnaryMinus;

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

    fn parameters(&self) -> Parameters {
        Parameters::new().with(
            "x",
            OrMatcher::default()
                .with(NumericMatcher::default())
                .with(IntervalMatcher::default()),
        )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        // Return type is the same as the input type
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

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

/// Unary plus operator: `+x` for numeric types
#[derive(Default, Clone)]
pub struct UnaryPlus;

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

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

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        // Return type is the same as the input type
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

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

/// Unary range prefix operator: `..x` creates a range with upper bound
#[derive(Default, Clone)]
pub struct UnaryRangePrefix;

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

    fn parameters(&self) -> Parameters {
        Parameters::new().with(
            "x",
            OrMatcher::default()
                .with(NumericMatcher::default())
                .with(IntervalMatcher::default())
                .with(ExactMatcher::of(TIMESTAMP)),
        )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        // Return type is a range of the input type
        Ok(Range::new(bindings.get_by_index(0)?.typ().clone()).into())
    }

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

/// Unary range postfix operator: `x..` creates a range with lower bound
#[derive(Default, Clone)]
pub struct UnaryRangePostfix;

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

    fn parameters(&self) -> Parameters {
        Parameters::new().with(
            "x",
            OrMatcher::default()
                .with(NumericMatcher::default())
                .with(IntervalMatcher::default())
                .with(ExactMatcher::of(TIMESTAMP)),
        )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        // Return type is a range of the input type
        Ok(Range::new(bindings.get_by_index(0)?.typ().clone()).into())
    }

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