use std::fmt;
use std::num::NonZeroUsize;

use regex::RegexBuilder;
use simd_csv::ByteRecord;

use crate::collections::HashMap;

use super::error::{ConcretizationError, EvaluationError, SpecifiedEvaluationError};
use super::functions::special::{RuntimeFunction as SpecialFunction, get_special_function};
use super::functions::{Function, get_function};
use super::parser::{Expr, FunctionCall, parse_expression};
use super::types::{
    BOUND_ARGUMENTS_CAPACITY, BoundArgument, BoundArguments, ColumIndexationBy, DynamicValue,
    EvaluationResult, FunctionArguments, HeadersIndex, LambdaArguments,
};

pub type GlobalNames = [&'static str];

#[derive(Debug, Default, Clone)]
pub struct GlobalVariables {
    slots: [DynamicValue; 2],
}

impl GlobalVariables {
    #[inline]
    fn get(&self, index: usize) -> Option<&DynamicValue> {
        self.slots.get(index)
    }

    #[inline]
    pub fn set_value(&mut self, index: usize, value: DynamicValue) {
        self.slots[index] = value;
    }

    #[inline]
    pub fn set<T: Into<DynamicValue>>(&mut self, index: usize, value: T) {
        self.set_value(index, value.into());
    }
}

// NOTE: I am use NonZeroUsize here to leverage niche optimizations and keep
// EvaluationContext 64 in size. This ultimately means the actual indices
// are offset by 1.
// I might revisit this if I find a convincing way to merge globals, lambda
// arguments and last value into the same value stack.
#[derive(Debug, Clone)]
pub struct EvaluationContext<'a> {
    row_index: Option<NonZeroUsize>,
    col_index: Option<NonZeroUsize>,
    pub record: &'a ByteRecord,
    pub headers_index: &'a HeadersIndex,
    pub globals: Option<&'a GlobalVariables>,
    pub lambda_variables: Option<&'a LambdaArguments>,
    last_value: Option<DynamicValue>,
}

impl<'a> EvaluationContext<'a> {
    #[inline]
    pub fn new(
        row_index: Option<usize>,
        record: &'a ByteRecord,
        headers_index: &'a HeadersIndex,
    ) -> Self {
        Self {
            row_index: row_index.map(|i| NonZeroUsize::new(i + 1).unwrap()),
            col_index: None,
            record,
            headers_index,
            globals: None,
            lambda_variables: None,
            last_value: None,
        }
    }

    #[inline]
    pub fn new_with_col_index(
        row_index: Option<usize>,
        col_index: Option<usize>,
        record: &'a ByteRecord,
        headers_index: &'a HeadersIndex,
    ) -> Self {
        Self {
            row_index: row_index.map(|i| NonZeroUsize::new(i + 1).unwrap()),
            col_index: col_index.map(|i| NonZeroUsize::new(i + 1).unwrap()),
            record,
            headers_index,
            globals: None,
            lambda_variables: None,
            last_value: None,
        }
    }

    #[inline]
    pub fn new_with_globals(
        row_index: Option<usize>,
        record: &'a ByteRecord,
        headers_index: &'a HeadersIndex,
        globals: &'a GlobalVariables,
    ) -> Self {
        let mut context = Self::new(row_index, record, headers_index);
        context.globals = Some(globals);

        context
    }

    #[inline]
    fn dummy(record: &'a ByteRecord, headers_index: &'a HeadersIndex) -> Self {
        Self {
            row_index: None,
            col_index: None,
            record,
            headers_index,
            globals: None,
            lambda_variables: None,
            last_value: None,
        }
    }

    #[inline]
    pub fn row_index(&self) -> Option<usize> {
        self.row_index.map(|i| i.get() - 1)
    }

    #[inline]
    pub fn col_index(&self) -> Option<usize> {
        self.col_index.map(|i| i.get() - 1)
    }

    #[inline(always)]
    pub fn evaluate(&self, expr: &ConcreteExpr) -> EvaluationResult {
        expr.evaluate(self)
    }

    #[inline]
    pub fn with_lambda_variables(&self, variables: &'a LambdaArguments) -> Self {
        Self {
            row_index: self.row_index,
            col_index: self.col_index,
            record: self.record,
            headers_index: self.headers_index,
            globals: self.globals,
            lambda_variables: Some(variables),
            last_value: self.last_value.clone(),
        }
    }

    #[inline]
    pub fn with_globals(&self, globals: &'a GlobalVariables) -> Self {
        Self {
            row_index: self.row_index,
            col_index: self.col_index,
            record: self.record,
            headers_index: self.headers_index,
            globals: Some(globals),
            lambda_variables: self.lambda_variables,
            last_value: self.last_value.clone(),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum ConcreteExpr {
    Column(usize),
    GlobalVariable(usize),
    Lambda(Vec<String>, Box<ConcreteExpr>),
    LambdaBinding(String),
    Value(DynamicValue),
    List(Vec<ConcreteExpr>),
    Map(Vec<(String, ConcreteExpr)>),
    Call(ConcreteFunctionCall),
    SpecialCall(ConcreteSpecialFunctionCall),
    Pipeline(Vec<ConcreteExpr>),
    Underscore,
}

// NOTE: the bind/evaluate distinction is still useful to propagate the calling
// function context when constructing specified errors.
impl ConcreteExpr {
    fn is_value(&self) -> bool {
        matches!(self, Self::Value(_))
    }

    pub fn used_column_indices(&self, scratch: &mut Vec<usize>) {
        match self {
            Self::Column(i) => {
                scratch.push(*i);
            }
            Self::Lambda(_, inner) => {
                inner.used_column_indices(scratch);
            }
            Self::List(values) => {
                for value in values {
                    value.used_column_indices(scratch);
                }
            }
            Self::Map(map) => {
                for (_, value) in map {
                    value.used_column_indices(scratch);
                }
            }
            Self::Call(call) => {
                for arg in call.args.iter() {
                    arg.used_column_indices(scratch);
                }
            }
            Self::SpecialCall(call) => {
                for arg in call.args.iter() {
                    arg.used_column_indices(scratch);
                }
            }
            Self::Pipeline(exprs) => {
                for expr in exprs {
                    expr.used_column_indices(scratch);
                }
            }
            _ => (),
        };
    }

    // NOTE: here we are not abiding by the DFS
    fn is_deeply_statically_evaluable(&self, bound: &Vec<String>) -> bool {
        match self {
            Self::Value(_) => true,
            Self::LambdaBinding(name) => bound.contains(name),
            Self::Lambda(names, expr) => {
                let mut new_bound = bound.clone();

                for name in names {
                    if !new_bound.contains(name) {
                        new_bound.push(name.to_string());
                    }
                }

                expr.is_deeply_statically_evaluable(&new_bound)
            }
            Self::Call(call) => call.is_statically_evaluable(bound),
            Self::SpecialCall(call) => call.is_statically_evaluable(bound),
            _ => false,
        }
    }

    fn as_column(&self) -> Option<usize> {
        match self {
            Self::Column(index) => Some(*index),
            _ => None,
        }
    }

    pub fn try_unwrap(self) -> Result<DynamicValue, ConcretizationError> {
        match self {
            Self::Value(v) => Ok(v),
            _ => Err(ConcretizationError::NotStaticallyAnalyzable),
        }
    }

    fn unwrap(self) -> DynamicValue {
        match self {
            Self::Value(v) => v,
            _ => panic!("cannot unwrap"),
        }
    }

    pub fn try_as_lambda(&self) -> Result<(&Vec<String>, &Self), EvaluationError> {
        match self {
            Self::Lambda(names, expr) => Ok((names, expr)),
            _ => Err(EvaluationError::InvalidLambda),
        }
    }

    fn bind<'s>(
        &'s self,
        context: &'s EvaluationContext,
    ) -> Result<BoundArgument<'s>, EvaluationError> {
        Ok(match self {
            Self::Value(value) => BoundArgument::Borrowed(value),
            Self::Column(index) => match context.record.get(*index) {
                None => return Err(EvaluationError::ColumnOutOfRange(*index)),
                Some(cell) => BoundArgument::Cell(cell),
            },
            Self::GlobalVariable(index) => {
                match context.globals.expect("globals were not set!").get(*index) {
                    None => return Err(EvaluationError::GlobalVariableOutOfRange(*index)),
                    Some(value) => BoundArgument::Borrowed(value),
                }
            }
            Self::LambdaBinding(name) => BoundArgument::Borrowed(
                context
                    .lambda_variables
                    .expect("lambda_variables MUST be set")
                    .get(name),
            ),
            Self::Underscore => match context.last_value.as_ref() {
                None => match context.col_index() {
                    Some(index) => match context.record.get(index) {
                        None => return Err(EvaluationError::ColumnOutOfRange(index)),
                        Some(cell) => BoundArgument::Cell(cell),
                    },
                    None => return Err(EvaluationError::UnfillableUnderscore),
                },
                Some(last_value) => BoundArgument::Borrowed(last_value),
            },
            Self::List(_)
            | Self::Map(_)
            | Self::Call(_)
            | Self::SpecialCall(_)
            | Self::Lambda(_, _)
            | Self::Pipeline(_) => unreachable!(),
        })
    }

    pub fn evaluate(&self, context: &EvaluationContext) -> EvaluationResult {
        match self {
            Self::Call(function_call) => function_call.run(context),
            Self::SpecialCall(function_call) => function_call.run(context),
            Self::List(items) => {
                let mut bound = Vec::with_capacity(items.len());

                for item in items {
                    bound.push(item.evaluate(context)?);
                }

                Ok(DynamicValue::from(bound))
            }
            Self::Map(pairs) => {
                let mut bound = HashMap::with_capacity(pairs.len());

                for (k, v) in pairs {
                    bound.insert(k.to_string(), v.evaluate(context)?);
                }

                Ok(DynamicValue::from(bound))
            }
            Self::Pipeline(pipeline) => {
                debug_assert!(!pipeline.is_empty());

                let mut pipeline_context = context.clone();

                for expr in pipeline {
                    let value = expr.evaluate(&pipeline_context)?;
                    pipeline_context.last_value = Some(value);
                }

                Ok(pipeline_context.last_value.unwrap())
            }
            _ => self
                .bind(context)
                .map(|arg| arg.into_owned())
                .map_err(|err| err.anonymous()),
        }
    }
}

#[derive(Clone, PartialEq)]
pub struct ConcreteFunctionCall {
    name: String,
    function: Function,
    args: Vec<ConcreteExpr>,
}

impl ConcreteFunctionCall {
    fn is_statically_evaluable(&self, bound: &Vec<String>) -> bool {
        // NOTE: nullary functions such as row_index() or uuid() usually
        // rely on some external implicit state and cannot be statically
        // evaluated.
        !self.args.is_empty()
            && self
                .args
                .iter()
                .all(|arg| arg.is_deeply_statically_evaluable(bound))
    }

    fn static_run(&self) -> EvaluationResult {
        let record = ByteRecord::new();
        let headers_index = HeadersIndex::default();

        self.run(&EvaluationContext::dummy(&record, &headers_index))
    }

    fn run<'s>(&'s self, context: &'s EvaluationContext) -> EvaluationResult {
        let mut bound_args = BoundArguments::new();

        for arg in self.args.iter() {
            match arg {
                ConcreteExpr::Call(sub_function_call) => {
                    bound_args.push(BoundArgument::Owned(sub_function_call.run(context)?));
                }
                ConcreteExpr::SpecialCall(sub_function_call) => {
                    bound_args.push(BoundArgument::Owned(sub_function_call.run(context)?));
                }
                ConcreteExpr::List(_) | ConcreteExpr::Map(_) => {
                    bound_args.push(BoundArgument::Owned(arg.evaluate(context)?))
                }
                _ => bound_args.push(arg.bind(context).map_err(|err| err.specify(&self.name))?),
            }
        }

        match (self.function)(bound_args) {
            Ok(value) => Ok(value),
            Err(err) => Err(err.specify(&self.name)),
        }
    }
}

// NOTE: in older rust versions, Debug cannot be derived
// correctly from `fn` and it will not compile without
// this custom `Debug` implementation
impl fmt::Debug for ConcreteFunctionCall {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ConcreteFunctionCall")
            .field("name", &self.name)
            .field("args", &self.args)
            .finish()
    }
}

#[derive(Clone, PartialEq)]
pub struct ConcreteSpecialFunctionCall {
    name: String,
    function: SpecialFunction,
    args: Vec<ConcreteExpr>,
}

impl ConcreteSpecialFunctionCall {
    fn is_statically_evaluable(&self, bound: &Vec<String>) -> bool {
        // NOTE: other special function are not suitable for late
        // statical evaluation.
        if [
            "col",
            "col?",
            "col_index",
            "col_index?",
            "cols",
            "header",
            "header?",
            "headers",
            "row_index",
            "prev_col",
            "next_col",
            "if",
            "unless",
            "warn",
            "and",
            "or",
        ]
        .contains(&self.name.as_str())
        {
            return false;
        }

        self.args
            .iter()
            .all(|arg| arg.is_deeply_statically_evaluable(bound))
    }

    fn static_run(&self) -> EvaluationResult {
        let record = ByteRecord::new();
        let headers_index = HeadersIndex::default();

        self.run(&EvaluationContext::dummy(&record, &headers_index))
    }

    fn run(&self, context: &EvaluationContext) -> EvaluationResult {
        (self.function)(context, &self.args)
    }
}

// NOTE: in older rust versions, Debug cannot be derived
// correctly from `fn` and it will not compile without
// this custom `Debug` implementation
impl fmt::Debug for ConcreteSpecialFunctionCall {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ConcreteSpecialFunctionCall")
            .field("name", &self.name)
            .field("args", &self.args)
            .finish()
    }
}

fn concretize_arguments(
    function_arguments: &FunctionArguments,
    parsed_args: Vec<(Option<String>, Expr)>,
    headers_index: &HeadersIndex,
    globals: Option<&GlobalNames>,
) -> Result<Vec<ConcreteExpr>, ConcretizationError> {
    let concrete_args = parsed_args
        .into_iter()
        .map(|(name, expr)| concretize_expression(expr, headers_index, globals).map(|r| (name, r)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(function_arguments
        .reorder(concrete_args)?
        .into_iter()
        .map(|opt| opt.unwrap_or(ConcreteExpr::Value(DynamicValue::None)))
        .collect())
}

fn concretize_call(
    call: FunctionCall,
    headers_index: &HeadersIndex,
    globals: Option<&GlobalNames>,
) -> Result<ConcreteExpr, ConcretizationError> {
    let function_name = &call.name;
    let actual_arity = call.args.len();

    if actual_arity > BOUND_ARGUMENTS_CAPACITY {
        return Err(ConcretizationError::TooManyArguments(actual_arity));
    }

    // Dealing with special functions
    if let Some((comptime_function, runtime_function, arguments)) =
        get_special_function(function_name)
    {
        arguments
            .validate_arity(actual_arity)
            .map_err(|invalid_arity| {
                ConcretizationError::InvalidArity(function_name.clone(), invalid_arity)
            })?;

        // Some function can be evaluated when concretizing if they
        // are statically analyzable.
        if let Some(function) = comptime_function {
            // NOTE: some function must be statically analyzable and will
            // yell if they don't have a runtime counterpart.
            if let Some(concrete_expr) = function(&call, headers_index)? {
                return Ok(concrete_expr);
            }
        }

        // TODO: at some point it might be nice to refactor comptime evaluation
        // to better take care of complex cases such as branching & trying.

        // NOTE: special cases wrt branching
        if function_name == "if" || function_name == "unless" {
            let condition = concretize_expression(call.args[0].clone().1, headers_index, globals)?;

            if let Ok(value) = condition.try_unwrap() {
                let path = if function_name == "if" {
                    if value.is_truthy() { 1 } else { 2 }
                } else if value.is_truthy() {
                    2
                } else {
                    1
                };

                return concretize_expression(call.args[path].clone().1, headers_index, globals);
            }
        }

        if function_name == "or" || function_name == "and" {
            for (_, arg) in call.args.iter() {
                if let Ok(condition) = concretize_expression(arg.clone(), headers_index, globals) {
                    if let Ok(value) = condition.try_unwrap() {
                        let test = if function_name == "or" {
                            value.is_truthy()
                        } else {
                            value.is_falsey()
                        };

                        if test {
                            return Ok(ConcreteExpr::Value(value));
                        }
                    } else {
                        break;
                    }
                } else {
                    break;
                }
            }
        }

        let concrete_args = concretize_arguments(&arguments, call.args, headers_index, globals);

        // NOTE: special case of bubbling-up exceptions
        if function_name == "try" && concrete_args.is_err() {
            return Ok(ConcreteExpr::Value(DynamicValue::None));
        }

        let concrete_call = ConcreteSpecialFunctionCall {
            name: function_name.clone(),
            function: runtime_function.expect("missing special function runtime"),
            args: concrete_args?,
        };

        if concrete_call.is_statically_evaluable(&vec![]) {
            match concrete_call.static_run() {
                Err(evaluation_error) => {
                    return Err(ConcretizationError::StaticEvaluationError(evaluation_error));
                }
                Ok(value) => return Ok(ConcreteExpr::Value(value)),
            };
        }

        return Ok(ConcreteExpr::SpecialCall(concrete_call));
    }

    Ok(match get_function(function_name) {
        None => return Err(ConcretizationError::UnknownFunction(function_name.clone())),
        Some((function, arguments)) => {
            arguments
                .validate_arity(actual_arity)
                .map_err(|invalid_arity| {
                    ConcretizationError::InvalidArity(function_name.clone(), invalid_arity)
                })?;

            let concrete_call = ConcreteFunctionCall {
                name: function_name.clone(),
                function,
                args: concretize_arguments(&arguments, call.args, headers_index, globals)?,
            };

            if concrete_call.is_statically_evaluable(&vec![]) {
                match concrete_call.static_run() {
                    Err(evaluation_error) => {
                        return Err(ConcretizationError::StaticEvaluationError(evaluation_error));
                    }
                    Ok(value) => return Ok(ConcreteExpr::Value(value)),
                };
            }

            ConcreteExpr::Call(concrete_call)
        }
    })
}

fn concretize_list(
    list: Vec<Expr>,
    headers_index: &HeadersIndex,
    globals: Option<&GlobalNames>,
) -> Result<ConcreteExpr, ConcretizationError> {
    let concrete_list = list
        .into_iter()
        .map(|item| concretize_expression(item, headers_index, globals))
        .collect::<Result<Vec<ConcreteExpr>, _>>()?;

    // NOTE: here we can collapse to a literal value
    Ok(if concrete_list.iter().all(|e| e.is_value()) {
        ConcreteExpr::Value(DynamicValue::from(
            concrete_list
                .into_iter()
                .map(|e| e.unwrap())
                .collect::<Vec<_>>(),
        ))
    } else {
        ConcreteExpr::List(concrete_list)
    })
}

fn concretize_map(
    map: Vec<(String, Expr)>,
    headers_index: &HeadersIndex,
    globals: Option<&GlobalNames>,
) -> Result<ConcreteExpr, ConcretizationError> {
    let concrete_map = map
        .into_iter()
        .map(|(k, v)| concretize_expression(v, headers_index, globals).map(|e| (k, e)))
        .collect::<Result<Vec<(String, ConcreteExpr)>, _>>()?;

    // NOTE: here we can collapse to a literal value
    Ok(if concrete_map.iter().all(|(_, e)| e.is_value()) {
        ConcreteExpr::Value(DynamicValue::from(
            concrete_map
                .into_iter()
                .map(|(k, e)| (k, e.unwrap()))
                .collect::<HashMap<_, _>>(),
        ))
    } else {
        ConcreteExpr::Map(concrete_map)
    })
}

pub fn concretize_expression(
    expr: Expr,
    headers_index: &HeadersIndex,
    globals: Option<&GlobalNames>,
) -> Result<ConcreteExpr, ConcretizationError> {
    Ok(match expr {
        Expr::Underscore => ConcreteExpr::Underscore,
        Expr::Null => ConcreteExpr::Value(DynamicValue::None),
        Expr::Bool(v) => ConcreteExpr::Value(DynamicValue::Boolean(v)),
        Expr::Float(v) => ConcreteExpr::Value(DynamicValue::Float(v)),
        Expr::Int(v) => ConcreteExpr::Value(DynamicValue::Integer(v)),
        Expr::Str(v) => ConcreteExpr::Value(DynamicValue::from(v)),
        Expr::BStr(v) => ConcreteExpr::Value(DynamicValue::from_owned_bytes(v)),
        Expr::Identifier(name, unsure) => {
            if let Some(g) = globals {
                if let Some(index) = g.iter().position(|n| *n == name) {
                    return Ok(ConcreteExpr::GlobalVariable(index));
                }
            }

            match headers_index.first_by_name(&name) {
                Some(index) => ConcreteExpr::Column(index),
                None => {
                    if unsure {
                        return Ok(ConcreteExpr::Value(DynamicValue::None));
                    }

                    return Err(ConcretizationError::ColumnNotFound(
                        ColumIndexationBy::Name(name.into_bytes()),
                        headers_index.is_headless(),
                    ));
                }
            }
        }
        Expr::Regex(pattern, case_insensitive) => match RegexBuilder::new(&pattern)
            .case_insensitive(case_insensitive)
            .build()
        {
            Ok(regex) => ConcreteExpr::Value(DynamicValue::from(regex)),
            Err(_) => return Err(ConcretizationError::InvalidRegex(pattern)),
        },
        Expr::Func(call) => concretize_call(call, headers_index, globals)?,
        Expr::List(list) => concretize_list(list, headers_index, globals)?,
        Expr::Map(map) => concretize_map(map, headers_index, globals)?,
        Expr::Lambda(names, expr) => ConcreteExpr::Lambda(
            names,
            Box::new(concretize_expression(*expr, headers_index, globals)?),
        ),
        Expr::Pipeline(pipeline) => ConcreteExpr::Pipeline(
            pipeline
                .into_iter()
                .map(|expr| concretize_expression(expr, headers_index, globals))
                .collect::<Result<Vec<_>, _>>()?,
        ),
        Expr::LambdaBinding(name) => ConcreteExpr::LambdaBinding(name),
        Expr::Slice(_) => unreachable!(),
    })
}

#[derive(Clone, Debug)]
pub struct Program {
    pub expr: ConcreteExpr,
    headers_index: HeadersIndex,
}

impl Program {
    pub fn parse(
        code: &str,
        headers: &ByteRecord,
        headless: bool,
    ) -> Result<Self, ConcretizationError> {
        let headers_index = HeadersIndex::new(headers, headless);

        let expr = match parse_expression(code) {
            Err(err) => return Err(ConcretizationError::ParseError(err)),
            Ok(parsed_expr) => concretize_expression(parsed_expr, &headers_index, None)?,
        };

        Ok(Self {
            expr,
            headers_index,
        })
    }

    pub fn parse_with_globals(
        code: &str,
        headers: &ByteRecord,
        headless: bool,
        globals: &GlobalNames,
    ) -> Result<Self, ConcretizationError> {
        let headers_index = HeadersIndex::new(headers, headless);

        let expr = match parse_expression(code) {
            Err(err) => return Err(ConcretizationError::ParseError(err)),
            Ok(parsed_expr) => concretize_expression(parsed_expr, &headers_index, Some(globals))?,
        };

        Ok(Self {
            expr,
            headers_index,
        })
    }

    pub fn run_with_record(
        &self,
        row_index: usize,
        record: &ByteRecord,
    ) -> Result<DynamicValue, SpecifiedEvaluationError> {
        EvaluationContext::new(Some(row_index), record, &self.headers_index).evaluate(&self.expr)
    }

    pub fn run_with_record_and_col_index(
        &self,
        row_index: usize,
        col_index: usize,
        record: &ByteRecord,
    ) -> Result<DynamicValue, SpecifiedEvaluationError> {
        EvaluationContext::new_with_col_index(
            Some(row_index),
            Some(col_index),
            record,
            &self.headers_index,
        )
        .evaluate(&self.expr)
    }

    pub fn run_with_record_and_globals(
        &self,
        row_index: usize,
        record: &ByteRecord,
        globals: &GlobalVariables,
    ) -> Result<DynamicValue, SpecifiedEvaluationError> {
        EvaluationContext::new_with_globals(Some(row_index), record, &self.headers_index, globals)
            .evaluate(&self.expr)
    }

    pub fn generate_key(
        &self,
        row_index: usize,
        record: &ByteRecord,
    ) -> Result<String, SpecifiedEvaluationError> {
        if let Some(index) = self.expr.as_column() {
            Ok(String::from_utf8(record[index].to_vec()).unwrap())
        } else {
            let value = self.run_with_record(row_index, record)?;
            Ok(value
                .try_as_str()
                .map(|s| s.to_string())
                .map_err(|err| err.anonymous())?)
        }
    }
}

#[cfg(test)]
mod tests {
    use std::mem::size_of;

    use super::super::error::RunError;
    use super::*;
    use jiff::{
        Timestamp, Zoned,
        civil::{Date, DateTime, Time},
        tz::TimeZone,
    };

    type TestResult = Result<DynamicValue, RunError>;

    fn b(string: &str) -> DynamicValue {
        DynamicValue::from(string.as_bytes())
    }

    fn concretize_code(code: &str) -> Result<ConcreteExpr, ConcretizationError> {
        let mut headers = ByteRecord::new();
        headers.push_field(b"name");
        headers.push_field(b"surname");
        headers.push_field(b"a");
        headers.push_field(b"b");

        let program = Program::parse(code, &headers, false)?;

        Ok(program.expr)
    }

    fn eval_code(code: &str) -> TestResult {
        let mut headers = ByteRecord::new();
        headers.push_field(b"name");
        headers.push_field(b"surname");
        headers.push_field(b"a");
        headers.push_field(b"b");

        let program = Program::parse(code, &headers, false).map_err(RunError::Prepare)?;

        let mut record = ByteRecord::new();
        record.push_field(b"john");
        record.push_field(b"SMITH");
        record.push_field(b"34");
        record.push_field(b"62");

        program
            .run_with_record(2, &record)
            .map_err(RunError::Evaluation)
    }

    #[test]
    fn test_sizes() {
        assert_eq!(size_of::<EvaluationContext>(), 64);
        assert_eq!(size_of::<ConcreteExpr>(), 64);
    }

    #[test]
    fn test_static_evaluation() {
        assert_eq!(
            concretize_code("1 + name"),
            Ok(ConcreteExpr::Call(ConcreteFunctionCall {
                name: "add".to_string(),
                function: get_function("add").unwrap().0,
                args: vec![
                    ConcreteExpr::Value(DynamicValue::Integer(1)),
                    ConcreteExpr::Column(0)
                ]
            }))
        );

        assert_eq!(
            concretize_code("1 + 2 * 4"),
            Ok(ConcreteExpr::Value(DynamicValue::Integer(9)))
        );
    }

    #[test]
    fn test_pipeline_optimization_correctness() {
        assert_eq!(
            eval_code("trim(a) | add(a, b) | trim(_) | add(a, b) | len(_)"),
            Ok(DynamicValue::Integer(2))
        );

        assert_eq!(
            eval_code("trim(a) | len(_) | add(b, _)"),
            Ok(DynamicValue::Integer(64))
        );
    }

    #[test]
    fn test_identifiers() {
        assert_eq!(eval_code("name"), Ok(b("john")));
        assert_eq!(eval_code("name?"), Ok(b("john")));
        assert_eq!(eval_code("full_name?"), Ok(DynamicValue::None));
    }

    #[test]
    fn test_row_index() {
        assert_eq!(eval_code("row_index() + 2"), Ok(DynamicValue::from(4)));
    }

    #[test]
    fn test_typeof() {
        assert_eq!(eval_code("typeof(name)"), Ok(DynamicValue::from("bytes")));
        assert_eq!(eval_code("TYPEOF(name)"), Ok(DynamicValue::from("bytes")));
        assert_eq!(
            eval_code("typeof(first(name))"),
            Ok(DynamicValue::from("string"))
        );
    }

    #[test]
    fn test_split_join() {
        assert_eq!(
            eval_code("split(name, 'o')"),
            Ok(DynamicValue::from(vec![
                DynamicValue::from("j"),
                DynamicValue::from("hn"),
            ]))
        );

        assert_eq!(
            eval_code("split(name, 'o', 1)"),
            Ok(DynamicValue::from(vec![
                DynamicValue::from("j"),
                DynamicValue::from("hn"),
            ]))
        );

        assert_eq!(
            eval_code("split(name, 'o') | join(_, '&')"),
            Ok(DynamicValue::from("j&hn"))
        )
    }

    #[test]
    fn test_arithmetics() {
        assert_eq!(eval_code("add(1, 2)"), Ok(DynamicValue::Integer(3)));
        assert_eq!(eval_code("add(1, 2, 3, 4)"), Ok(DynamicValue::Integer(10)));
        assert_eq!(eval_code("sub(1, 2)"), Ok(DynamicValue::Integer(-1)));
        assert_eq!(eval_code("mul(1, 2)"), Ok(DynamicValue::Integer(2)));
        assert_eq!(eval_code("mul(3, 1.5)"), Ok(DynamicValue::Float(4.5)));
        assert_eq!(eval_code("div(3, 2)"), Ok(DynamicValue::Float(1.5)));
        assert_eq!(eval_code("idiv(4.5, 2)"), Ok(DynamicValue::Integer(2)));
        assert_eq!(eval_code("idiv(-4.5, 2)"), Ok(DynamicValue::Integer(-3)));
    }

    #[test]
    fn test_lower() {
        assert_eq!(eval_code("lower(surname)"), Ok(b("smith")));
    }

    #[test]
    fn test_upper() {
        assert_eq!(eval_code("upper(name)"), Ok(b("JOHN")));
    }

    #[test]
    fn test_count() {
        assert_eq!(eval_code("count(name, 'h')"), Ok(DynamicValue::Integer(1)));
    }

    #[test]
    fn test_concat() {
        assert_eq!(
            eval_code("concat(name, ' ', lower(surname))"),
            Ok(DynamicValue::from("john smith"))
        );
    }

    #[test]
    fn test_bool() {
        assert_eq!(eval_code("not(true)"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("and(true, false)"), Ok(DynamicValue::from(false)));
        assert_eq!(
            eval_code("and(true, true, false)"),
            Ok(DynamicValue::from(false))
        );
        assert_eq!(eval_code("or(true, false)"), Ok(DynamicValue::from(true)));
        assert_eq!(
            eval_code("or(false, false, true)"),
            Ok(DynamicValue::from(true))
        );
    }

    #[test]
    fn test_pathjoin() {
        assert_eq!(
            eval_code("pathjoin('one', 'two', 'three')"),
            Ok(DynamicValue::from(if cfg!(windows) {
                "one\\two\\three"
            } else {
                "one/two/three"
            }))
        );
    }

    #[test]
    fn test_first() {
        assert_eq!(eval_code("first(name)"), Ok(DynamicValue::from("j")));
        assert_eq!(
            eval_code("first(split(name, 'h', 1))"),
            Ok(DynamicValue::from("jo"))
        );
    }

    #[test]
    fn test_last() {
        assert_eq!(eval_code("last(name)"), Ok(DynamicValue::from("n")));
        assert_eq!(
            eval_code("last(split(name, 'o', 1))"),
            Ok(DynamicValue::from("hn"))
        );
    }

    #[test]
    fn test_get() {
        assert_eq!(eval_code("get('test', 0)"), Ok(DynamicValue::from("t")));
        assert_eq!(eval_code("get('test', 7, 4)"), Ok(DynamicValue::from(4)));
        assert_eq!(eval_code("'test'[1]"), Ok(DynamicValue::from("e")));

        assert_eq!(
            eval_code("get({'one': {'two': [1, 2, 3]}}, ['one', 'two', 1])"),
            Ok(DynamicValue::from(2))
        );
    }

    #[test]
    fn test_slice() {
        assert_eq!(
            eval_code("slice('abcde', 2)"),
            Ok(DynamicValue::from("cde"))
        );
        assert_eq!(eval_code("'abcde'[2:]"), Ok(DynamicValue::from("cde")));
        assert_eq!(
            eval_code("slice('abcde', -2)"),
            Ok(DynamicValue::from("de"))
        );
        assert_eq!(
            eval_code("slice('abcde', -1, 3)"),
            Ok(DynamicValue::from(""))
        );
        assert_eq!(
            eval_code("slice('abcde', -1, -3)"),
            Ok(DynamicValue::from(""))
        );
        assert_eq!(
            eval_code("slice('abcde', 1, 3)"),
            Ok(DynamicValue::from("bc"))
        );
        assert_eq!(eval_code("'abcde'[1:3]"), Ok(DynamicValue::from("bc")));
        assert_eq!(
            eval_code("slice('abcde', 1, -2)"),
            Ok(DynamicValue::from("bc"))
        );
        assert_eq!(eval_code("slice('abcde', 5)"), Ok(DynamicValue::from("")));
        assert_eq!(eval_code("slice('abcde', 10)"), Ok(DynamicValue::from("")));
        assert_eq!(
            eval_code("slice('abcde', -10)"),
            Ok(DynamicValue::from("abcde"))
        );
        assert_eq!(
            eval_code("slice('abcde', 10, -20)"),
            Ok(DynamicValue::from(""))
        );
    }

    #[test]
    fn test_trim() {
        assert_eq!(eval_code("trim(' test ')"), Ok(DynamicValue::from("test")));
        assert_eq!(
            eval_code("ltrim(' test ')"),
            Ok(DynamicValue::from("test "))
        );
        assert_eq!(
            eval_code("rtrim(' test ')"),
            Ok(DynamicValue::from(" test"))
        );

        assert_eq!(eval_code("trim('test', 't')"), Ok(DynamicValue::from("es")));
        assert_eq!(
            eval_code("ltrim('test', 't')"),
            Ok(DynamicValue::from("est"))
        );
        assert_eq!(
            eval_code("rtrim('test', 't')"),
            Ok(DynamicValue::from("tes"))
        );
    }

    #[test]
    fn test_abs() {
        assert_eq!(eval_code("abs(-5)"), Ok(DynamicValue::Integer(5)));
        assert_eq!(eval_code("abs(-5.0)"), Ok(DynamicValue::Float(5.0)));
    }

    #[test]
    fn test_contains() {
        assert_eq!(
            eval_code("contains('hello', 'ell')"),
            Ok(DynamicValue::from(true))
        );
        assert_eq!(
            eval_code("contains('hello', /l{2}/)"),
            Ok(DynamicValue::from(true))
        );
        assert_eq!(
            eval_code("contains('hello', /l{3}/)"),
            Ok(DynamicValue::from(false))
        );
        assert_eq!(
            eval_code("contains('hello', /L{2}/i)"),
            Ok(DynamicValue::from(true))
        );
    }

    #[test]
    fn test_replace() {
        assert_eq!(
            eval_code("replace('hello', 'l', 't')"),
            Ok(DynamicValue::from("hetto"))
        );
        assert_eq!(
            eval_code("replace('hello', /l+O/i, 't')"),
            Ok(DynamicValue::from("het"))
        );
        assert_eq!(
            eval_code("replace('hello', /(he)llo/i, '$1')"),
            Ok(DynamicValue::from("he"))
        );
    }

    #[test]
    fn test_escape_regex() {
        assert_eq!(
            eval_code("escape_regex('(hello)')"),
            Ok(DynamicValue::from(r"\(hello\)"))
        );
        assert_eq!(
            eval_code("escape_regex('Hey. How are doing ?')"),
            Ok(DynamicValue::from(r"Hey\. How are doing \?"))
        );
    }

    #[test]
    fn test_if() {
        assert_eq!(eval_code("if(true, 3, 2)"), Ok(DynamicValue::from(3)));
        assert_eq!(
            eval_code("if(eq(2, 2), add(1, 3), sub(1, 0))"),
            Ok(DynamicValue::from(4))
        );
        assert_eq!(
            eval_code("if(if(if(true, true), true), if(false, add(1, 2), add(4, 5)))"),
            Ok(DynamicValue::from(9))
        );
    }

    #[test]
    fn test_unless() {
        assert_eq!(eval_code("unless(true, 3, 2)"), Ok(DynamicValue::from(2)));
    }

    #[test]
    fn test_neg() {
        assert_eq!(eval_code("neg(-1)"), Ok(DynamicValue::from(1)));
        assert_eq!(eval_code("neg(1)"), Ok(DynamicValue::from(-1)));
        assert_eq!(eval_code("neg(1.5)"), Ok(DynamicValue::from(-1.5)));
        assert_eq!(eval_code("neg(0)"), Ok(DynamicValue::from(0)));
        assert_eq!(eval_code("neg(0.0)"), Ok(DynamicValue::from(0.0)));
    }

    #[test]
    fn test_compact() {
        assert_eq!(
            eval_code("compact(split('', '|'))"),
            Ok(DynamicValue::from(vec![]))
        );
    }

    #[test]
    fn test_col() {
        assert_eq!(eval_code("col('name')"), Ok(b("john")));
        assert_eq!(eval_code("col(1)"), Ok(b("SMITH")));
        assert_eq!(eval_code("col(1.0)"), Ok(b("SMITH")));
        assert_eq!(eval_code("col('surname', 0)"), Ok(b("SMITH")));
        assert_eq!(
            eval_code("col('surname', 1)"),
            Err(RunError::Prepare(ConcretizationError::ColumnNotFound(
                ColumIndexationBy::NameAndNth(b"surname".to_vec(), 1),
                false
            )))
        );
        assert_eq!(eval_code("col(concat('sur', 'name'))"), Ok(b("SMITH")));
        assert_eq!(
            eval_code("col(concat('sur', 'name'), 1 - 1)"),
            Ok(b("SMITH"))
        );
    }

    #[test]
    fn test_fmt() {
        assert_eq!(
            eval_code("fmt('Hello {}', 'John')"),
            Ok(DynamicValue::from("Hello John"))
        );
        assert_eq!(
            eval_code("fmt('Hello {} {}', 'John', 45)"),
            Ok(DynamicValue::from("Hello John 45"))
        );
    }

    #[test]
    fn test_ceil_floor_round() {
        assert_eq!(eval_code("ceil(2.3)"), Ok(DynamicValue::from(3)));
        assert_eq!(eval_code("ceil(4.8)"), Ok(DynamicValue::from(5)));
        assert_eq!(eval_code("floor(2.3)"), Ok(DynamicValue::from(2)));
        assert_eq!(eval_code("floor(-3.6)"), Ok(DynamicValue::from(-4)));
        assert_eq!(eval_code("round(2.3)"), Ok(DynamicValue::from(2)));
        assert_eq!(eval_code("round(3)"), Ok(DynamicValue::from(3)));
    }

    #[test]
    fn test_log_sqrt() {
        assert_eq!(eval_code("log(1)"), Ok(DynamicValue::from(0.0)));
        assert_eq!(
            eval_code("log(3.5)"),
            Ok(DynamicValue::from(1.252762968495368))
        );
        assert_eq!(eval_code("sqrt(4)"), Ok(DynamicValue::from(2.0)));
        assert_eq!(eval_code("sqrt(100)"), Ok(DynamicValue::from(10.0)));
    }

    #[test]
    fn test_md5() {
        assert_eq!(
            eval_code("md5('test')"),
            Ok(DynamicValue::from("098f6bcd4621d373cade4e832627b4f6"))
        );
    }

    #[test]
    fn test_pow() {
        assert_eq!(eval_code("pow(2, 4)"), Ok(DynamicValue::from(16)));
    }

    #[test]
    fn test_mod() {
        assert_eq!(eval_code("mod(8, 2)"), Ok(DynamicValue::from(0)));
    }

    #[test]
    fn test_infix_operators() {
        assert_eq!(eval_code("1 + 2"), Ok(DynamicValue::from(3)));
        assert_eq!(eval_code("1 - 2"), Ok(DynamicValue::from(-1)));
        assert_eq!(eval_code("2 * 2"), Ok(DynamicValue::from(4)));
        assert_eq!(eval_code("1 / 2"), Ok(DynamicValue::from(0.5)));
        assert_eq!(eval_code("1 // 2"), Ok(DynamicValue::from(0)));
        assert_eq!(eval_code("2 ** 4"), Ok(DynamicValue::from(16)));
        assert_eq!(eval_code("8 % 2"), Ok(DynamicValue::from(0)));

        assert_eq!(eval_code("'he' ++ 'llo'"), Ok(DynamicValue::from("hello")));

        assert_eq!(eval_code("true && false"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("true and false"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("true || false"), Ok(DynamicValue::from(true)));
        assert_eq!(eval_code("true or false"), Ok(DynamicValue::from(true)));

        assert_eq!(
            eval_code("true && (true && (false || true) || false && false) && false"),
            Ok(DynamicValue::from(false))
        );

        assert_eq!(eval_code("'h' in 'hello'"), Ok(DynamicValue::from(true)));
        assert_eq!(
            eval_code("'h' not in 'hello'"),
            Ok(DynamicValue::from(false))
        );
        assert_eq!(eval_code("3 in [1, 2, 3]"), Ok(DynamicValue::from(true)));
        assert_eq!(
            eval_code("'3' in ['1', '2', '3']"),
            Ok(DynamicValue::from(true))
        );
        assert_eq!(
            eval_code("3 in ['1', '2', '3']"),
            Ok(DynamicValue::from(false))
        );
        assert_eq!(eval_code("'3' in [1, 2, 3]"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("4 in [1, 2, 3]"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("'4' in [1, 2, 3]"), Ok(DynamicValue::from(false)));

        assert_eq!(eval_code("!true"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("!!!true"), Ok(DynamicValue::from(false)));

        assert_eq!(eval_code("1 == 2"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("1 > 2"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("1 >= 2"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("1 < 2"), Ok(DynamicValue::from(true)));
        assert_eq!(eval_code("1 <= 2"), Ok(DynamicValue::from(true)));
        assert_eq!(eval_code("1 != 2"), Ok(DynamicValue::from(true)));

        assert_eq!(eval_code("'a' eq 'b'"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("'a' gt 'b'"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("'a' ge 'b'"), Ok(DynamicValue::from(false)));
        assert_eq!(eval_code("'a' lt 'b'"), Ok(DynamicValue::from(true)));
        assert_eq!(eval_code("'a' le 'b'"), Ok(DynamicValue::from(true)));
        assert_eq!(eval_code("'a' ne 'b'"), Ok(DynamicValue::from(true)));
    }

    #[test]
    fn test_bytesize() {
        assert_eq!(
            eval_code("bytesize(2510)"),
            Ok(DynamicValue::from("2.5 kB"))
        );
        assert_eq!(eval_code("bytesize(0)"), Ok(DynamicValue::from("0 B")));
    }

    #[test]
    fn test_map() {
        assert_eq!(
            eval_code("{hello: 'world'} | get(_, 'hello')"),
            Ok(DynamicValue::from("world"))
        );

        assert_eq!(eval_code("{hello: name} | get(_, 'hello')"), Ok(b("john")));
    }

    #[test]
    fn test_json() {
        assert_eq!(
            eval_code("parse_json('[1, 2, 3]') | get(_, 1)"),
            Ok(DynamicValue::from(2))
        );

        assert_eq!(
            eval_code("parse_json('{\"one\": 34}') | get(_, 'one')"),
            Ok(DynamicValue::from(34))
        );
    }

    #[test]
    fn test_minmax() {
        assert_eq!(eval_code("min(1, 2, -5, 4)"), Ok(DynamicValue::from(-5)));
        assert_eq!(eval_code("max(1, 2, -5, 4)"), Ok(DynamicValue::from(4)));

        assert_eq!(
            eval_code("argmin([1, 2, -5, 4])"),
            Ok(DynamicValue::from(2))
        );
        assert_eq!(
            eval_code("argmin([1, 2, -5, 4], ['a', 'b', 'c', 'd'])"),
            Ok(DynamicValue::from("c"))
        );
        assert_eq!(
            eval_code("argmin([1, 2, -5, 4], ['a'])"),
            Ok(DynamicValue::None)
        );
        assert_eq!(eval_code("argmin([a, b])"), Ok(DynamicValue::from(0)));

        assert_eq!(
            eval_code("argmax([1, 2, -5, 4])"),
            Ok(DynamicValue::from(3))
        );
        assert_eq!(
            eval_code("argmax([1, 2, -5, 4], ['a', 'b', 'c', 'd'])"),
            Ok(DynamicValue::from("d"))
        );
        assert_eq!(
            eval_code("argmax([1, 2, -5, 4], ['a'])"),
            Ok(DynamicValue::None)
        );
        assert_eq!(eval_code("argmax([a, b])"), Ok(DynamicValue::from(1)));
    }

    // #[test]
    // fn test_timestamp() {
    //     let tz = TimeZone::UTC;
    //     let timestamp = Timestamp::from_second(1645805387).unwrap();
    //     let zoned = timestamp.to_zoned(tz);

    //     assert_eq!(
    //         eval_code("timestamp(1645805387)"),
    //         Ok(DynamicValue::from(zoned))
    //     )
    // }

    // #[test]
    // fn test_timestamp_ms() {
    //     let tz = TimeZone::UTC;
    //     let timestamp = Timestamp::from_millisecond(1645805387000).unwrap();
    //     let zoned = timestamp.to_zoned(tz);

    //     assert_eq!(
    //         eval_code("timestamp_ms(1645805387000)"),
    //         Ok(DynamicValue::from(zoned))
    //     )
    // }

    #[test]
    fn test_datetime() {
        let timestamp: Timestamp = "2024-07-11T01:14:00Z".parse().unwrap();
        let datetime: DateTime = "2024-07-11T01:14:00".parse().unwrap();
        let zoned = timestamp.in_tz("Europe/Paris").unwrap();

        assert_eq!(
            eval_code("'2024-07-11'.datetime()"),
            Ok(DynamicValue::from(
                datetime.with().time(Time::default()).build().unwrap()
            ))
        );

        assert_eq!(
            eval_code("'2024-07-11T01:14:00'.datetime()"),
            Ok(DynamicValue::from(datetime))
        );

        assert_eq!(
            eval_code("'2024-07-11T01:14:00Z'.datetime()"),
            Ok(DynamicValue::from(timestamp.to_zoned(TimeZone::UTC)))
        );

        assert_eq!(
            eval_code("'2024-07-11T01:14:00'.datetime()"),
            Ok(DynamicValue::from(datetime))
        );

        assert_eq!(
            eval_code("'2024-07-11T03:14:00[Europe/Paris]'.datetime()"),
            Ok(DynamicValue::from(zoned.clone()))
        );

        assert_eq!(
            eval_code("'2024-07-11T03:14:00+02[Europe/Paris]'.datetime()"),
            Ok(DynamicValue::from(zoned.clone()))
        );

        assert!(eval_code("'2024-07-11T03:14:00+05[Europe/Paris]'.datetime()").is_err());

        assert_eq!(
            eval_code("datetime('2024-07-11 03h14 Europe/Paris', '%F %Hh%M %Q')",),
            Ok(DynamicValue::from(zoned.clone()))
        );

        assert!(eval_code("datetime('2024-07-11 03h14 Europe/Glougou', '%F %Hh%M %Q')",).is_err());

        assert_eq!(
            eval_code("datetime('2024-07-11 01h14', '%F %Hh%M')",),
            Ok(DynamicValue::from(datetime))
        );
    }

    #[test]
    fn test_date() {
        let date: Date = "1999-01-31".parse().unwrap();

        assert!(eval_code("'20'.date()").is_err());

        assert_eq!(
            eval_code("'1999-01-31'.date()"),
            Ok(DynamicValue::from(date))
        );

        assert_eq!(
            eval_code("'99/01/31'.date('%y/%m/%d')"),
            Ok(DynamicValue::from(date))
        );
    }

    #[test]
    fn test_time() {
        let time: Time = "01:45".parse().unwrap();

        assert!(eval_code("'test'.time()").is_err());

        assert_eq!(eval_code("'01:45'.time()"), Ok(DynamicValue::from(time)));

        assert_eq!(
            eval_code("'01/45'.time('%H/%M')"),
            Ok(DynamicValue::from(time))
        );
    }

    #[test]
    fn test_without_timezone() {
        assert!(eval_code("'2024-07-11T03:14:00'.datetime().without_timezone()").is_err());
        assert_eq!(
            eval_code("'2024-07-11T03:14:00[Europe/Paris]'.datetime().without_timezone()",),
            Ok(DynamicValue::from(
                "2024-07-11T03:14:00".parse::<DateTime>().unwrap()
            ))
        );
    }

    #[test]
    fn test_with_timezone() {
        assert!(
            eval_code("'2024-07-11T03:14:00[Europe/Paris]'.datetime().with_timezone('UTC')")
                .is_err()
        );
        assert_eq!(
            eval_code("'2024-07-11T03:14:00'.datetime().with_timezone('UTC')",),
            Ok(DynamicValue::from(
                "2024-07-11T03:14:00[UTC]".parse::<Zoned>().unwrap()
            ))
        );
    }

    #[test]
    fn test_to_timezone() {
        assert!(eval_code("'2024-07-11T03:14:00'.datetime().to_timezone('UTC')").is_err());
        assert_eq!(
            eval_code("'2024-07-11T03:14:00[Europe/Paris]'.datetime().to_timezone('UTC')"),
            Ok(DynamicValue::from(
                "2024-07-11T01:14:00[UTC]".parse::<Zoned>().unwrap()
            ))
        );
    }

    #[test]
    fn test_year_month_day() {
        assert_eq!(
            eval_code("year_month_day('2024-07-11T03:14:00[Europe/Paris]')"),
            Ok(DynamicValue::from("2024-07-11"))
        );
        assert_eq!(
            eval_code("year_month_day('20240711 03:14[CET]')"),
            Ok(DynamicValue::from("2024-07-11"))
        );
        assert_eq!(
            eval_code("year_month_day('2024-07-11')"),
            Ok(DynamicValue::from("2024-07-11"))
        );
        assert_eq!(
            eval_code("year_month_day('2024-07-11T03:14:00')"),
            Ok(DynamicValue::from("2024-07-11"))
        );
        assert_eq!(
            eval_code("ymd('2024-07-11T03:14:00')"),
            Ok(DynamicValue::from("2024-07-11"))
        );
    }

    #[test]
    fn test_month_day() {
        assert_eq!(
            eval_code("month_day('2024-07-11T03:14:00[Europe/Paris]')"),
            Ok(DynamicValue::from("07-11"))
        );
        assert_eq!(
            eval_code("month_day('2024-07-11')"),
            Ok(DynamicValue::from("07-11"))
        );
        assert_eq!(
            eval_code("month_day('20240711 03:14[CET]')"),
            Ok(DynamicValue::from("07-11"))
        );
    }

    #[test]
    fn test_month() {
        assert_eq!(
            eval_code("month('2024-07-11')"),
            Ok(DynamicValue::from("07"))
        );
        assert_eq!(
            eval_code("month('2024-07-11T03:14:00[Europe/Paris]')"),
            Ok(DynamicValue::from("07"))
        );
        assert_eq!(
            eval_code("month('20240711 03:14[CET]')"),
            Ok(DynamicValue::from("07"))
        );
    }

    #[test]
    fn test_year() {
        assert_eq!(
            eval_code("year('2024-07-11')"),
            Ok(DynamicValue::from("2024"))
        );
        assert_eq!(
            eval_code("year('2024-07-11T03:14:00[Europe/Paris]')"),
            Ok(DynamicValue::from("2024"))
        );
        assert_eq!(
            eval_code("year('20240711 03:14[CET]')"),
            Ok(DynamicValue::from("2024"))
        );
    }

    #[test]
    fn test_year_month() {
        assert_eq!(
            eval_code("year_month('2024-07-11')"),
            Ok(DynamicValue::from("2024-07"))
        );
    }

    #[test]
    fn test_strftime() {
        assert_eq!(
            eval_code("strftime('2024-07-11T03:14:00', '%Y')"),
            Ok(DynamicValue::from("2024"))
        );

        assert_eq!(
            eval_code("strftime(datetime('September 5, 2024', '%B %d, %Y'), '%Y')"),
            Ok(DynamicValue::from("2024"))
        );
    }
}
