use sea_query::{Expr, Func, FunctionCall, Iden};

/// ClickHouse-specific functions.
///
/// Use the static methods on `ChFunc` to build [`FunctionCall`] values
/// that can be used in expressions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ChFunc {
    // -- DateTime functions --
    ToDateTime,
    ToDateTime64,
    ToDate,
    ToDate32,
    ToYear,
    ToMonth,
    ToDayOfMonth,
    ToHour,
    ToMinute,
    ToSecond,
    ToStartOfDay,
    ToStartOfMonth,
    ToStartOfYear,
    ToStartOfWeek,
    ToStartOfHour,
    ToStartOfMinute,
    ToStartOfInterval,
    DateDiff,
    DateAdd,
    DateSub,
    FormatDateTime,
    ParseDateTime,
    Now,
    Now64,
    Today,
    Yesterday,
    ToUnixTimestamp,
    FromUnixTimestamp,
    ToYYYYMM,
    ToYYYYMMDD,

    // -- Array functions --
    ArrayJoin,
    GroupArray,
    GroupUniqArray,
    ArrayMap,
    ArrayFilter,
    ArraySort,
    ArrayReverseSort,
    ArrayReverse,
    ArrayFlatten,
    ArrayCompact,
    ArrayDistinct,
    ArrayConcat,
    ArraySlice,
    Has,
    HasAll,
    HasAny,
    IndexOf,
    ArrayCount,
    ArraySum,
    ArrayAvg,
    ArrayMin,
    ArrayMax,
    Length,

    // -- String functions --
    ToString,
    SplitByChar,
    SplitByString,
    ReplaceAll,
    ReplaceOne,
    Concat,
    ConcatWithSeparator,
    Substring,
    TrimBoth,
    TrimLeft,
    TrimRight,
    Base64Encode,
    Base64Decode,
    LengthUTF8,
    Reverse,
    ExtractAll,
    Match,
    Like,
    NotLike,
    MultiSearchAny,
    Position,

    // -- Type conversion functions --
    ToUInt8,
    ToUInt16,
    ToUInt32,
    ToUInt64,
    ToInt8,
    ToInt16,
    ToInt32,
    ToInt64,
    ToFloat32,
    ToFloat64,
    ToDecimal32,
    ToDecimal64,
    ToDecimal128,
    ToFixedString,
    ToUUID,
    ToIPv4,
    ToIPv6,
    ToTypeName,
    ReinterpretAsString,
    ReinterpretAsUInt64,
    Cast,

    // -- Hash functions --
    SipHash64,
    SipHash128,
    CityHash64,
    XxHash64,
    Murmur3_64,
    Md5,
    Sha256,
    HalfMd5,

    // -- Aggregate functions --
    Uniq,
    UniqExact,
    UniqCombined,
    UniqHLL12,
    ArgMin,
    ArgMax,
    Quantile,
    QuantileExact,
    Quantiles,
    TopK,
    SumIf,
    CountIf,
    AvgIf,
    MinIf,
    MaxIf,
    AnyFunc,
    AnyLast,
    GroupBitAnd,
    GroupBitOr,
    GroupBitXor,
    Median,

    // -- Conditional functions --
    If,
    MultiIf,

    // -- URL functions --
    Domain,
    TopLevelDomain,
    Path,
    Protocol,
    ExtractURLParameter,

    // -- JSON functions --
    JSONExtract,
    JSONExtractString,
    JSONExtractUInt,
    JSONExtractInt,
    JSONExtractFloat,
    JSONExtractBool,
    JSONExtractArrayRaw,
    JSONHas,
    JSONLength,
    JSONType,

    // -- Math functions --
    Exp,
    Log,
    Log2,
    Log10,
    Sqrt,
    Cbrt,
    Power,
    IntDiv,
    IntDivOrZero,
    ModuloOrZero,

    // -- Null functions --
    IsNull,
    IsNotNull,
    IfNull,
    NullIf,
    AssumeNotNull,
    ToNullable,

    // -- Other common functions --
    GenerateUUIDv4,
    RunningDifference,
    Neighbor,
    RowNumberInAllBlocks,
    Version,
    Uptime,
}

impl ChFunc {
    pub fn sql_name(&self) -> &'static str {
        match self {
            ChFunc::ToDateTime => "toDateTime",
            ChFunc::ToDateTime64 => "toDateTime64",
            ChFunc::ToDate => "toDate",
            ChFunc::ToDate32 => "toDate32",
            ChFunc::ToYear => "toYear",
            ChFunc::ToMonth => "toMonth",
            ChFunc::ToDayOfMonth => "toDayOfMonth",
            ChFunc::ToHour => "toHour",
            ChFunc::ToMinute => "toMinute",
            ChFunc::ToSecond => "toSecond",
            ChFunc::ToStartOfDay => "toStartOfDay",
            ChFunc::ToStartOfMonth => "toStartOfMonth",
            ChFunc::ToStartOfYear => "toStartOfYear",
            ChFunc::ToStartOfWeek => "toStartOfWeek",
            ChFunc::ToStartOfHour => "toStartOfHour",
            ChFunc::ToStartOfMinute => "toStartOfMinute",
            ChFunc::ToStartOfInterval => "toStartOfInterval",
            ChFunc::DateDiff => "dateDiff",
            ChFunc::DateAdd => "dateAdd",
            ChFunc::DateSub => "dateSub",
            ChFunc::FormatDateTime => "formatDateTime",
            ChFunc::ParseDateTime => "parseDateTime",
            ChFunc::Now => "now",
            ChFunc::Now64 => "now64",
            ChFunc::Today => "today",
            ChFunc::Yesterday => "yesterday",
            ChFunc::ToUnixTimestamp => "toUnixTimestamp",
            ChFunc::FromUnixTimestamp => "fromUnixTimestamp",
            ChFunc::ToYYYYMM => "toYYYYMM",
            ChFunc::ToYYYYMMDD => "toYYYYMMDD",
            ChFunc::ArrayJoin => "arrayJoin",
            ChFunc::GroupArray => "groupArray",
            ChFunc::GroupUniqArray => "groupUniqArray",
            ChFunc::ArrayMap => "arrayMap",
            ChFunc::ArrayFilter => "arrayFilter",
            ChFunc::ArraySort => "arraySort",
            ChFunc::ArrayReverseSort => "arrayReverseSort",
            ChFunc::ArrayReverse => "arrayReverse",
            ChFunc::ArrayFlatten => "arrayFlatten",
            ChFunc::ArrayCompact => "arrayCompact",
            ChFunc::ArrayDistinct => "arrayDistinct",
            ChFunc::ArrayConcat => "arrayConcat",
            ChFunc::ArraySlice => "arraySlice",
            ChFunc::Has => "has",
            ChFunc::HasAll => "hasAll",
            ChFunc::HasAny => "hasAny",
            ChFunc::IndexOf => "indexOf",
            ChFunc::ArrayCount => "arrayCount",
            ChFunc::ArraySum => "arraySum",
            ChFunc::ArrayAvg => "arrayAvg",
            ChFunc::ArrayMin => "arrayMin",
            ChFunc::ArrayMax => "arrayMax",
            ChFunc::Length => "length",
            ChFunc::ToString => "toString",
            ChFunc::SplitByChar => "splitByChar",
            ChFunc::SplitByString => "splitByString",
            ChFunc::ReplaceAll => "replaceAll",
            ChFunc::ReplaceOne => "replaceOne",
            ChFunc::Concat => "concat",
            ChFunc::ConcatWithSeparator => "concatWithSeparator",
            ChFunc::Substring => "substring",
            ChFunc::TrimBoth => "trimBoth",
            ChFunc::TrimLeft => "trimLeft",
            ChFunc::TrimRight => "trimRight",
            ChFunc::Base64Encode => "base64Encode",
            ChFunc::Base64Decode => "base64Decode",
            ChFunc::LengthUTF8 => "lengthUTF8",
            ChFunc::Reverse => "reverse",
            ChFunc::ExtractAll => "extractAll",
            ChFunc::Match => "match",
            ChFunc::Like => "like",
            ChFunc::NotLike => "notLike",
            ChFunc::MultiSearchAny => "multiSearchAny",
            ChFunc::Position => "position",
            ChFunc::ToUInt8 => "toUInt8",
            ChFunc::ToUInt16 => "toUInt16",
            ChFunc::ToUInt32 => "toUInt32",
            ChFunc::ToUInt64 => "toUInt64",
            ChFunc::ToInt8 => "toInt8",
            ChFunc::ToInt16 => "toInt16",
            ChFunc::ToInt32 => "toInt32",
            ChFunc::ToInt64 => "toInt64",
            ChFunc::ToFloat32 => "toFloat32",
            ChFunc::ToFloat64 => "toFloat64",
            ChFunc::ToDecimal32 => "toDecimal32",
            ChFunc::ToDecimal64 => "toDecimal64",
            ChFunc::ToDecimal128 => "toDecimal128",
            ChFunc::ToFixedString => "toFixedString",
            ChFunc::ToUUID => "toUUID",
            ChFunc::ToIPv4 => "toIPv4",
            ChFunc::ToIPv6 => "toIPv6",
            ChFunc::ToTypeName => "toTypeName",
            ChFunc::ReinterpretAsString => "reinterpretAsString",
            ChFunc::ReinterpretAsUInt64 => "reinterpretAsUInt64",
            ChFunc::Cast => "CAST",
            ChFunc::SipHash64 => "sipHash64",
            ChFunc::SipHash128 => "sipHash128",
            ChFunc::CityHash64 => "cityHash64",
            ChFunc::XxHash64 => "xxHash64",
            ChFunc::Murmur3_64 => "murmurHash3_64",
            ChFunc::Md5 => "MD5",
            ChFunc::Sha256 => "SHA256",
            ChFunc::HalfMd5 => "halfMD5",
            ChFunc::Uniq => "uniq",
            ChFunc::UniqExact => "uniqExact",
            ChFunc::UniqCombined => "uniqCombined",
            ChFunc::UniqHLL12 => "uniqHLL12",
            ChFunc::ArgMin => "argMin",
            ChFunc::ArgMax => "argMax",
            ChFunc::Quantile => "quantile",
            ChFunc::QuantileExact => "quantileExact",
            ChFunc::Quantiles => "quantiles",
            ChFunc::TopK => "topK",
            ChFunc::SumIf => "sumIf",
            ChFunc::CountIf => "countIf",
            ChFunc::AvgIf => "avgIf",
            ChFunc::MinIf => "minIf",
            ChFunc::MaxIf => "maxIf",
            ChFunc::AnyFunc => "any",
            ChFunc::AnyLast => "anyLast",
            ChFunc::GroupBitAnd => "groupBitAnd",
            ChFunc::GroupBitOr => "groupBitOr",
            ChFunc::GroupBitXor => "groupBitXor",
            ChFunc::Median => "median",
            ChFunc::If => "if",
            ChFunc::MultiIf => "multiIf",
            ChFunc::Domain => "domain",
            ChFunc::TopLevelDomain => "topLevelDomain",
            ChFunc::Path => "path",
            ChFunc::Protocol => "protocol",
            ChFunc::ExtractURLParameter => "extractURLParameter",
            ChFunc::JSONExtract => "JSONExtract",
            ChFunc::JSONExtractString => "JSONExtractString",
            ChFunc::JSONExtractUInt => "JSONExtractUInt",
            ChFunc::JSONExtractInt => "JSONExtractInt",
            ChFunc::JSONExtractFloat => "JSONExtractFloat",
            ChFunc::JSONExtractBool => "JSONExtractBool",
            ChFunc::JSONExtractArrayRaw => "JSONExtractArrayRaw",
            ChFunc::JSONHas => "JSONHas",
            ChFunc::JSONLength => "JSONLength",
            ChFunc::JSONType => "JSONType",
            ChFunc::Exp => "exp",
            ChFunc::Log => "log",
            ChFunc::Log2 => "log2",
            ChFunc::Log10 => "log10",
            ChFunc::Sqrt => "sqrt",
            ChFunc::Cbrt => "cbrt",
            ChFunc::Power => "power",
            ChFunc::IntDiv => "intDiv",
            ChFunc::IntDivOrZero => "intDivOrZero",
            ChFunc::ModuloOrZero => "moduloOrZero",
            ChFunc::IsNull => "isNull",
            ChFunc::IsNotNull => "isNotNull",
            ChFunc::IfNull => "ifNull",
            ChFunc::NullIf => "nullIf",
            ChFunc::AssumeNotNull => "assumeNotNull",
            ChFunc::ToNullable => "toNullable",
            ChFunc::GenerateUUIDv4 => "generateUUIDv4",
            ChFunc::RunningDifference => "runningDifference",
            ChFunc::Neighbor => "neighbor",
            ChFunc::RowNumberInAllBlocks => "rowNumberInAllBlocks",
            ChFunc::Version => "version",
            ChFunc::Uptime => "uptime",
        }
    }
}

/// Iden wrapper for ClickHouse function names (written unquoted).
struct ChFuncIden(&'static str);

impl Iden for ChFuncIden {
    fn unquoted(&self) -> &str {
        self.0
    }
}

fn ch_func(func: ChFunc) -> FunctionCall {
    Func::cust(ChFuncIden(func.sql_name()))
}

impl ChFunc {
    pub fn to_date_time<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToDateTime).arg(expr)
    }
    pub fn to_date<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToDate).arg(expr)
    }
    pub fn to_year<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToYear).arg(expr)
    }
    pub fn to_month<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToMonth).arg(expr)
    }
    pub fn to_day_of_month<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToDayOfMonth).arg(expr)
    }
    pub fn to_start_of_day<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToStartOfDay).arg(expr)
    }
    pub fn to_start_of_month<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToStartOfMonth).arg(expr)
    }
    pub fn to_start_of_year<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToStartOfYear).arg(expr)
    }
    pub fn date_diff<T: Into<Expr>>(unit: T, start: T, end: T) -> FunctionCall {
        ch_func(ChFunc::DateDiff).args([unit.into(), start.into(), end.into()])
    }
    pub fn format_date_time<T: Into<Expr>>(expr: T, format: T) -> FunctionCall {
        ch_func(ChFunc::FormatDateTime).args([expr.into(), format.into()])
    }
    pub fn now() -> FunctionCall {
        ch_func(ChFunc::Now)
    }
    pub fn today() -> FunctionCall {
        ch_func(ChFunc::Today)
    }
    pub fn yesterday() -> FunctionCall {
        ch_func(ChFunc::Yesterday)
    }
    pub fn to_unix_timestamp<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToUnixTimestamp).arg(expr)
    }
    pub fn to_yyyymm<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToYYYYMM).arg(expr)
    }
    pub fn to_yyyymmdd<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToYYYYMMDD).arg(expr)
    }
    pub fn array_join<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ArrayJoin).arg(expr)
    }
    pub fn group_array<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::GroupArray).arg(expr)
    }
    pub fn group_uniq_array<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::GroupUniqArray).arg(expr)
    }
    pub fn has<T: Into<Expr>>(array: T, element: T) -> FunctionCall {
        ch_func(ChFunc::Has).args([array.into(), element.into()])
    }
    pub fn index_of<T: Into<Expr>>(array: T, element: T) -> FunctionCall {
        ch_func(ChFunc::IndexOf).args([array.into(), element.into()])
    }
    pub fn length<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Length).arg(expr)
    }
    pub fn array_concat<I, T>(arrays: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::ArrayConcat);
        for a in arrays {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn array_distinct<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ArrayDistinct).arg(expr)
    }
    pub fn array_flatten<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ArrayFlatten).arg(expr)
    }
    pub fn to_uint32<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToUInt32).arg(expr)
    }
    pub fn to_uint64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToUInt64).arg(expr)
    }
    pub fn to_int32<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToInt32).arg(expr)
    }
    pub fn to_int64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToInt64).arg(expr)
    }
    pub fn to_float64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToFloat64).arg(expr)
    }
    pub fn to_string<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToString).arg(expr)
    }
    pub fn to_type_name<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToTypeName).arg(expr)
    }
    pub fn to_uuid<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToUUID).arg(expr)
    }
    pub fn to_ipv4<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToIPv4).arg(expr)
    }
    pub fn to_ipv6<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToIPv6).arg(expr)
    }
    pub fn replace_all<T: Into<Expr>>(haystack: T, pattern: T, replacement: T) -> FunctionCall {
        ch_func(ChFunc::ReplaceAll).args([haystack.into(), pattern.into(), replacement.into()])
    }
    pub fn split_by_char<T: Into<Expr>>(separator: T, string: T) -> FunctionCall {
        ch_func(ChFunc::SplitByChar).args([separator.into(), string.into()])
    }
    pub fn split_by_string<T: Into<Expr>>(separator: T, string: T) -> FunctionCall {
        ch_func(ChFunc::SplitByString).args([separator.into(), string.into()])
    }
    pub fn sip_hash64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::SipHash64).arg(expr)
    }
    pub fn city_hash64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::CityHash64).arg(expr)
    }
    pub fn xx_hash64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::XxHash64).arg(expr)
    }
    pub fn uniq<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Uniq).arg(expr)
    }
    pub fn uniq_exact<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::UniqExact).arg(expr)
    }
    pub fn arg_min<T: Into<Expr>>(arg: T, val: T) -> FunctionCall {
        ch_func(ChFunc::ArgMin).args([arg.into(), val.into()])
    }
    pub fn arg_max<T: Into<Expr>>(arg: T, val: T) -> FunctionCall {
        ch_func(ChFunc::ArgMax).args([arg.into(), val.into()])
    }
    pub fn sum_if<T: Into<Expr>>(column: T, condition: T) -> FunctionCall {
        ch_func(ChFunc::SumIf).args([column.into(), condition.into()])
    }
    pub fn count_if<T: Into<Expr>>(condition: T) -> FunctionCall {
        ch_func(ChFunc::CountIf).arg(condition)
    }
    pub fn r#if<T: Into<Expr>>(condition: T, then_expr: T, else_expr: T) -> FunctionCall {
        ch_func(ChFunc::If).args([condition.into(), then_expr.into(), else_expr.into()])
    }
    pub fn is_null<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::IsNull).arg(expr)
    }
    pub fn is_not_null<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::IsNotNull).arg(expr)
    }
    pub fn assume_not_null<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::AssumeNotNull).arg(expr)
    }
    pub fn to_nullable<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToNullable).arg(expr)
    }
    pub fn json_extract_string<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONExtractString);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_extract<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONExtract);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn generate_uuid_v4() -> FunctionCall {
        ch_func(ChFunc::GenerateUUIDv4)
    }
    pub fn domain<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Domain).arg(expr)
    }
    pub fn top_level_domain<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::TopLevelDomain).arg(expr)
    }
    pub fn running_difference<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::RunningDifference).arg(expr)
    }

    // -- DateTime functions (missing) --
    pub fn to_date_time64<T: Into<Expr>>(expr: T, precision: T) -> FunctionCall {
        ch_func(ChFunc::ToDateTime64).args([expr.into(), precision.into()])
    }
    pub fn to_date32<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToDate32).arg(expr)
    }
    pub fn to_hour<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToHour).arg(expr)
    }
    pub fn to_minute<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToMinute).arg(expr)
    }
    pub fn to_second<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToSecond).arg(expr)
    }
    pub fn to_start_of_week<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToStartOfWeek).arg(expr)
    }
    pub fn to_start_of_hour<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToStartOfHour).arg(expr)
    }
    pub fn to_start_of_minute<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToStartOfMinute).arg(expr)
    }
    pub fn to_start_of_interval<T: Into<Expr>>(expr: T, interval: T) -> FunctionCall {
        ch_func(ChFunc::ToStartOfInterval).args([expr.into(), interval.into()])
    }
    pub fn date_add<T: Into<Expr>>(unit: T, interval: T, start: T) -> FunctionCall {
        ch_func(ChFunc::DateAdd).args([unit.into(), interval.into(), start.into()])
    }
    pub fn date_sub<T: Into<Expr>>(unit: T, interval: T, start: T) -> FunctionCall {
        ch_func(ChFunc::DateSub).args([unit.into(), interval.into(), start.into()])
    }
    pub fn parse_date_time<T: Into<Expr>>(string: T, format: T) -> FunctionCall {
        ch_func(ChFunc::ParseDateTime).args([string.into(), format.into()])
    }
    pub fn now64() -> FunctionCall {
        ch_func(ChFunc::Now64)
    }
    pub fn from_unix_timestamp<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::FromUnixTimestamp).arg(expr)
    }

    // -- Array functions (missing) --
    pub fn array_map<T: Into<Expr>>(func: T, array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayMap).args([func.into(), array.into()])
    }
    pub fn array_filter<T: Into<Expr>>(func: T, array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayFilter).args([func.into(), array.into()])
    }
    pub fn array_sort<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArraySort).arg(array)
    }
    pub fn array_reverse_sort<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayReverseSort).arg(array)
    }
    pub fn array_reverse<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayReverse).arg(array)
    }
    pub fn array_compact<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayCompact).arg(array)
    }
    pub fn array_slice<T: Into<Expr>>(array: T, offset: T, length: T) -> FunctionCall {
        ch_func(ChFunc::ArraySlice).args([array.into(), offset.into(), length.into()])
    }
    pub fn has_all<T: Into<Expr>>(array: T, elements: T) -> FunctionCall {
        ch_func(ChFunc::HasAll).args([array.into(), elements.into()])
    }
    pub fn has_any<T: Into<Expr>>(array: T, elements: T) -> FunctionCall {
        ch_func(ChFunc::HasAny).args([array.into(), elements.into()])
    }
    pub fn array_count<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayCount).arg(array)
    }
    pub fn array_sum<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArraySum).arg(array)
    }
    pub fn array_avg<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayAvg).arg(array)
    }
    pub fn array_min<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayMin).arg(array)
    }
    pub fn array_max<T: Into<Expr>>(array: T) -> FunctionCall {
        ch_func(ChFunc::ArrayMax).arg(array)
    }

    // -- String functions (missing) --
    pub fn replace_one<T: Into<Expr>>(haystack: T, pattern: T, replacement: T) -> FunctionCall {
        ch_func(ChFunc::ReplaceOne).args([haystack.into(), pattern.into(), replacement.into()])
    }
    pub fn concat<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::Concat);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn concat_with_separator<T: Into<Expr>>(separator: T, expr1: T, expr2: T) -> FunctionCall {
        ch_func(ChFunc::ConcatWithSeparator).args([separator.into(), expr1.into(), expr2.into()])
    }
    pub fn substring<T: Into<Expr>>(string: T, offset: T, length: T) -> FunctionCall {
        ch_func(ChFunc::Substring).args([string.into(), offset.into(), length.into()])
    }
    pub fn trim_both<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::TrimBoth).arg(expr)
    }
    pub fn trim_left<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::TrimLeft).arg(expr)
    }
    pub fn trim_right<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::TrimRight).arg(expr)
    }
    pub fn base64_encode<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Base64Encode).arg(expr)
    }
    pub fn base64_decode<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Base64Decode).arg(expr)
    }
    pub fn length_utf8<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::LengthUTF8).arg(expr)
    }
    pub fn reverse<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Reverse).arg(expr)
    }
    pub fn extract_all<T: Into<Expr>>(haystack: T, pattern: T) -> FunctionCall {
        ch_func(ChFunc::ExtractAll).args([haystack.into(), pattern.into()])
    }
    pub fn match_func<T: Into<Expr>>(haystack: T, pattern: T) -> FunctionCall {
        ch_func(ChFunc::Match).args([haystack.into(), pattern.into()])
    }
    pub fn like_func<T: Into<Expr>>(haystack: T, pattern: T) -> FunctionCall {
        ch_func(ChFunc::Like).args([haystack.into(), pattern.into()])
    }
    pub fn not_like<T: Into<Expr>>(haystack: T, pattern: T) -> FunctionCall {
        ch_func(ChFunc::NotLike).args([haystack.into(), pattern.into()])
    }
    pub fn multi_search_any<T: Into<Expr>>(haystack: T, needles: T) -> FunctionCall {
        ch_func(ChFunc::MultiSearchAny).args([haystack.into(), needles.into()])
    }
    pub fn position<T: Into<Expr>>(haystack: T, needle: T) -> FunctionCall {
        ch_func(ChFunc::Position).args([haystack.into(), needle.into()])
    }

    // -- Type conversion functions (missing) --
    pub fn to_uint8<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToUInt8).arg(expr)
    }
    pub fn to_uint16<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToUInt16).arg(expr)
    }
    pub fn to_int8<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToInt8).arg(expr)
    }
    pub fn to_int16<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToInt16).arg(expr)
    }
    pub fn to_float32<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ToFloat32).arg(expr)
    }
    pub fn to_decimal32<T: Into<Expr>>(expr: T, scale: T) -> FunctionCall {
        ch_func(ChFunc::ToDecimal32).args([expr.into(), scale.into()])
    }
    pub fn to_decimal64<T: Into<Expr>>(expr: T, scale: T) -> FunctionCall {
        ch_func(ChFunc::ToDecimal64).args([expr.into(), scale.into()])
    }
    pub fn to_decimal128<T: Into<Expr>>(expr: T, scale: T) -> FunctionCall {
        ch_func(ChFunc::ToDecimal128).args([expr.into(), scale.into()])
    }
    pub fn to_fixed_string<T: Into<Expr>>(expr: T, n: T) -> FunctionCall {
        ch_func(ChFunc::ToFixedString).args([expr.into(), n.into()])
    }
    pub fn reinterpret_as_string<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ReinterpretAsString).arg(expr)
    }
    pub fn reinterpret_as_uint64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::ReinterpretAsUInt64).arg(expr)
    }
    pub fn cast<T: Into<Expr>>(expr: T, r#type: T) -> FunctionCall {
        ch_func(ChFunc::Cast).args([expr.into(), r#type.into()])
    }

    // -- Hash functions (missing) --
    pub fn sip_hash128<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::SipHash128).arg(expr)
    }
    pub fn murmur3_64<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Murmur3_64).arg(expr)
    }
    pub fn md5<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Md5).arg(expr)
    }
    pub fn sha256<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Sha256).arg(expr)
    }
    pub fn half_md5<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::HalfMd5).arg(expr)
    }

    // -- Aggregate functions (missing) --
    pub fn uniq_combined<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::UniqCombined).arg(expr)
    }
    pub fn uniq_hll12<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::UniqHLL12).arg(expr)
    }
    pub fn quantile<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Quantile).arg(expr)
    }
    pub fn quantile_exact<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::QuantileExact).arg(expr)
    }
    pub fn quantiles<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::Quantiles);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn top_k<T: Into<Expr>>(n: T) -> FunctionCall {
        ch_func(ChFunc::TopK).arg(n)
    }
    pub fn avg_if<T: Into<Expr>>(column: T, condition: T) -> FunctionCall {
        ch_func(ChFunc::AvgIf).args([column.into(), condition.into()])
    }
    pub fn min_if<T: Into<Expr>>(column: T, condition: T) -> FunctionCall {
        ch_func(ChFunc::MinIf).args([column.into(), condition.into()])
    }
    pub fn max_if<T: Into<Expr>>(column: T, condition: T) -> FunctionCall {
        ch_func(ChFunc::MaxIf).args([column.into(), condition.into()])
    }
    pub fn any_func<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::AnyFunc).arg(expr)
    }
    pub fn any_last<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::AnyLast).arg(expr)
    }
    pub fn group_bit_and<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::GroupBitAnd).arg(expr)
    }
    pub fn group_bit_or<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::GroupBitOr).arg(expr)
    }
    pub fn group_bit_xor<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::GroupBitXor).arg(expr)
    }
    pub fn median<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Median).arg(expr)
    }

    // -- Conditional functions (missing) --
    pub fn multi_if<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::MultiIf);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }

    // -- URL functions (missing) --
    pub fn path<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Path).arg(expr)
    }
    pub fn protocol<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Protocol).arg(expr)
    }
    pub fn extract_url_parameter<T: Into<Expr>>(url: T, name: T) -> FunctionCall {
        ch_func(ChFunc::ExtractURLParameter).args([url.into(), name.into()])
    }

    // -- JSON functions (missing) --
    pub fn json_extract_uint<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONExtractUInt);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_extract_int<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONExtractInt);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_extract_float<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONExtractFloat);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_extract_bool<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONExtractBool);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_extract_array_raw<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONExtractArrayRaw);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_has<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONHas);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_length<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONLength);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }
    pub fn json_type<I, T>(args: I) -> FunctionCall
    where
        I: IntoIterator<Item = T>,
        T: Into<Expr>,
    {
        let mut fc = ch_func(ChFunc::JSONType);
        for a in args {
            fc = fc.arg(a);
        }
        fc
    }

    // -- Math functions (missing) --
    pub fn exp<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Exp).arg(expr)
    }
    pub fn log<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Log).arg(expr)
    }
    pub fn log2<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Log2).arg(expr)
    }
    pub fn log10<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Log10).arg(expr)
    }
    pub fn sqrt<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Sqrt).arg(expr)
    }
    pub fn cbrt<T: Into<Expr>>(expr: T) -> FunctionCall {
        ch_func(ChFunc::Cbrt).arg(expr)
    }
    pub fn power<T: Into<Expr>>(base: T, exponent: T) -> FunctionCall {
        ch_func(ChFunc::Power).args([base.into(), exponent.into()])
    }
    pub fn int_div<T: Into<Expr>>(a: T, b: T) -> FunctionCall {
        ch_func(ChFunc::IntDiv).args([a.into(), b.into()])
    }
    pub fn int_div_or_zero<T: Into<Expr>>(a: T, b: T) -> FunctionCall {
        ch_func(ChFunc::IntDivOrZero).args([a.into(), b.into()])
    }
    pub fn modulo_or_zero<T: Into<Expr>>(a: T, b: T) -> FunctionCall {
        ch_func(ChFunc::ModuloOrZero).args([a.into(), b.into()])
    }

    // -- Null functions (missing) --
    pub fn if_null<T: Into<Expr>>(expr: T, alt: T) -> FunctionCall {
        ch_func(ChFunc::IfNull).args([expr.into(), alt.into()])
    }
    pub fn null_if<T: Into<Expr>>(expr1: T, expr2: T) -> FunctionCall {
        ch_func(ChFunc::NullIf).args([expr1.into(), expr2.into()])
    }

    // -- Other functions (missing) --
    pub fn neighbor<T: Into<Expr>>(column: T, offset: T) -> FunctionCall {
        ch_func(ChFunc::Neighbor).args([column.into(), offset.into()])
    }
    pub fn row_number_in_all_blocks() -> FunctionCall {
        ch_func(ChFunc::RowNumberInAllBlocks)
    }
    pub fn version() -> FunctionCall {
        ch_func(ChFunc::Version)
    }
    pub fn uptime() -> FunctionCall {
        ch_func(ChFunc::Uptime)
    }
}
