//! DateTime functions

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters};
use crate::types::matcher::ExactMatcher;
use crate::types::{Type, DOUBLE, INT, INTERVAL, STRING, TIMESTAMP};

/// now() - Current timestamp.
#[derive(Default, Clone)]
pub struct Now;

impl FunctionDef for Now {
    fn name(&self) -> &'static str {
        "now"
    }

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

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

    fn is_deterministic(&self) -> bool {
        false
    }
}

/// today() - Start of current day.
#[derive(Default, Clone)]
pub struct Today;

impl FunctionDef for Today {
    fn name(&self) -> &'static str {
        "today"
    }

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

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

    fn is_deterministic(&self) -> bool {
        false
    }
}

/// yesterday() - Start of previous day.
#[derive(Default, Clone)]
pub struct Yesterday;

impl FunctionDef for Yesterday {
    fn name(&self) -> &'static str {
        "yesterday"
    }

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

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

    fn is_deterministic(&self) -> bool {
        false
    }
}

/// tomorrow() - Start of next day.
#[derive(Default, Clone)]
pub struct Tomorrow;

impl FunctionDef for Tomorrow {
    fn name(&self) -> &'static str {
        "tomorrow"
    }

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

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

    fn is_deterministic(&self) -> bool {
        false
    }
}

/// ts(string) - Parse timestamp from string.
#[derive(Default, Clone)]
pub struct Ts;

impl FunctionDef for Ts {
    fn name(&self) -> &'static str {
        "ts"
    }

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

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

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

/// year(timestamp) - Extract year from timestamp.
#[derive(Default, Clone)]
pub struct Year;

impl FunctionDef for Year {
    fn name(&self) -> &'static str {
        "year"
    }

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

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

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

/// month(timestamp) - Extract month from timestamp.
#[derive(Default, Clone)]
pub struct Month;

impl FunctionDef for Month {
    fn name(&self) -> &'static str {
        "month"
    }

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

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

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

/// day(timestamp) - Extract day from timestamp.
#[derive(Default, Clone)]
pub struct Day;

impl FunctionDef for Day {
    fn name(&self) -> &'static str {
        "day"
    }

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

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

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

/// day_of_week(timestamp) - Extract day of week from timestamp (1=Sunday, 7=Saturday).
#[derive(Default, Clone)]
pub struct DayOfWeek;

impl FunctionDef for DayOfWeek {
    fn name(&self) -> &'static str {
        "day_of_week"
    }

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

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

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

/// hour(timestamp) - Extract hour from timestamp.
#[derive(Default, Clone)]
pub struct Hour;

impl FunctionDef for Hour {
    fn name(&self) -> &'static str {
        "hour"
    }

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

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

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

/// minute(timestamp) - Extract minute from timestamp.
#[derive(Default, Clone)]
pub struct Minute;

impl FunctionDef for Minute {
    fn name(&self) -> &'static str {
        "minute"
    }

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

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

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

/// second(timestamp) - Extract second from timestamp.
#[derive(Default, Clone)]
pub struct Second;

impl FunctionDef for Second {
    fn name(&self) -> &'static str {
        "second"
    }

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

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

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

/// at_timezone(timestamp, timezone) - Convert timestamp to specified timezone.
#[derive(Default, Clone)]
pub struct AtTimezone;

impl FunctionDef for AtTimezone {
    fn name(&self) -> &'static str {
        "at_timezone"
    }

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

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

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

/// to_millis(interval) - Convert interval to milliseconds.
#[derive(Default, Clone)]
pub struct ToMillis;

impl FunctionDef for ToMillis {
    fn name(&self) -> &'static str {
        "to_millis"
    }

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

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

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

/// to_nanos(interval) - Convert interval to nanoseconds.
#[derive(Default, Clone)]
pub struct ToNanos;

impl FunctionDef for ToNanos {
    fn name(&self) -> &'static str {
        "to_nanos"
    }

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

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

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

/// from_millis(millis) - Create interval from milliseconds.
#[derive(Default, Clone)]
pub struct FromMillis;

impl FunctionDef for FromMillis {
    fn name(&self) -> &'static str {
        "from_millis"
    }

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

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

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

/// from_nanos(nanos) - Create interval from nanoseconds.
#[derive(Default, Clone)]
pub struct FromNanos;

impl FunctionDef for FromNanos {
    fn name(&self) -> &'static str {
        "from_nanos"
    }

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

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

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

/// from_unixtime_seconds(seconds) - Create timestamp from Unix epoch seconds.
#[derive(Default, Clone)]
pub struct FromUnixtimeSeconds;

impl FunctionDef for FromUnixtimeSeconds {
    fn name(&self) -> &'static str {
        "from_unixtime_seconds"
    }

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

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

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

/// from_unixtime_millis(millis) - Create timestamp from Unix epoch milliseconds.
#[derive(Default, Clone)]
pub struct FromUnixtimeMillis;

impl FunctionDef for FromUnixtimeMillis {
    fn name(&self) -> &'static str {
        "from_unixtime_millis"
    }

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

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

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

/// from_unixtime_micros(micros) - Create timestamp from Unix epoch microseconds.
#[derive(Default, Clone)]
pub struct FromUnixtimeMicros;

impl FunctionDef for FromUnixtimeMicros {
    fn name(&self) -> &'static str {
        "from_unixtime_micros"
    }

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

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

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

/// from_unixtime_nanos(nanos) - Create timestamp from Unix epoch nanoseconds.
#[derive(Default, Clone)]
pub struct FromUnixtimeNanos;

impl FunctionDef for FromUnixtimeNanos {
    fn name(&self) -> &'static str {
        "from_unixtime_nanos"
    }

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

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

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

/// to_unixtime(timestamp) - Convert timestamp to Unix epoch seconds as double.
#[derive(Default, Clone)]
pub struct ToUnixtime;

impl FunctionDef for ToUnixtime {
    fn name(&self) -> &'static str {
        "to_unixtime"
    }

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

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

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