//! Window functions

use std::any::TypeId;

use crate::func::def::{FunctionDef, ParameterBindingProvider, Parameters, SpecialPosition};
use crate::tree::ast::expression::{BooleanLiteral, Expression};
use crate::types::matcher::{AnyMatcher, ExactMatcher};
use crate::types::{Type, BOOLEAN, DOUBLE, INT};

/// row_number() - Assigns a unique sequential integer to each row.
#[derive(Default, Clone)]
pub struct RowNumber;

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

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

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// rank() - Assigns a rank to each row, with gaps for ties.
#[derive(Default, Clone)]
pub struct Rank;

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

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

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// dense_rank() - Assigns a rank to each row, without gaps for ties.
#[derive(Default, Clone)]
pub struct DenseRank;

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

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

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// lag(expression, offset, ignore_nulls) - Returns value from a preceding row.
#[derive(Default, Clone)]
pub struct Lag;

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

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("expression", AnyMatcher::default())
            .with("offset", ExactMatcher::of(INT))
            .with_default(
                "ignore_nulls",
                ExactMatcher::of(BOOLEAN),
                Expression::from_kind(BooleanLiteral { value: true }),
            )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// lead(expression, offset, ignore_nulls) - Returns value from a following row.
#[derive(Default, Clone)]
pub struct Lead;

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

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("expression", AnyMatcher::default())
            .with("offset", ExactMatcher::of(INT))
            .with_default(
                "ignore_nulls",
                ExactMatcher::of(BOOLEAN),
                Expression::from_kind(BooleanLiteral { value: true }),
            )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// first_value(expression, ignore_nulls) - Returns the first value in the window frame.
#[derive(Default, Clone)]
pub struct FirstValue;

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

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("expression", AnyMatcher::default())
            .with_default(
                "ignore_nulls",
                ExactMatcher::of(BOOLEAN),
                Expression::from_kind(BooleanLiteral { value: true }),
            )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// last_value(expression, ignore_nulls) - Returns the last value in the window frame.
#[derive(Default, Clone)]
pub struct LastValue;

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

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("expression", AnyMatcher::default())
            .with_default(
                "ignore_nulls",
                ExactMatcher::of(BOOLEAN),
                Expression::from_kind(BooleanLiteral { value: true }),
            )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// nth_value(expression, n, ignore_nulls) - Returns the nth value in the window frame.
#[derive(Default, Clone)]
pub struct NthValue;

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

    fn parameters(&self) -> Parameters {
        Parameters::new()
            .with("expression", AnyMatcher::default())
            .with("n", ExactMatcher::of(INT))
            .with_default(
                "ignore_nulls",
                ExactMatcher::of(BOOLEAN),
                Expression::from_kind(BooleanLiteral { value: true }),
            )
    }

    fn return_type(&self, bindings: &dyn ParameterBindingProvider) -> anyhow::Result<Type> {
        Ok(bindings.get_by_index(0)?.typ().clone())
    }

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// cume_dist() - Calculates the cumulative distribution of a value.
#[derive(Default, Clone)]
pub struct CumeDist;

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

    fn parameters(&self) -> Parameters {
        Parameters::new()
    }

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

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}

/// percent_rank() - Calculates the relative rank of a row.
#[derive(Default, Clone)]
pub struct PercentRank;

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

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

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

    fn special_position(&self) -> Option<SpecialPosition> {
        Some(SpecialPosition::Window)
    }
}
