use sea_query::{Alias, ColumnDef};

/// Extension trait for [`ColumnDef`] adding ClickHouse-specific column types.
///
/// These types have no direct equivalent in the standard `ColumnType` enum
/// and are rendered using `ColumnType::Custom` under the hood.
pub trait ClickhouseColumnDefExt {
    fn int128(&mut self) -> &mut Self;
    fn int256(&mut self) -> &mut Self;
    fn uint128(&mut self) -> &mut Self;
    fn uint256(&mut self) -> &mut Self;
    fn float32(&mut self) -> &mut Self;
    fn float64(&mut self) -> &mut Self;
    fn decimal32(&mut self, scale: u32) -> &mut Self;
    fn decimal64(&mut self, scale: u32) -> &mut Self;
    fn decimal128(&mut self, scale: u32) -> &mut Self;
    fn decimal256(&mut self, scale: u32) -> &mut Self;
    fn date32(&mut self) -> &mut Self;
    fn date_time64(&mut self, precision: u32) -> &mut Self;
    fn date_time64_tz(&mut self, precision: u32, timezone: &str) -> &mut Self;
    fn date_time_tz(&mut self, timezone: &str) -> &mut Self;
    fn fixed_string(&mut self, length: u32) -> &mut Self;
    fn low_cardinality(&mut self, inner: &str) -> &mut Self;
    fn clickhouse_nullable(&mut self, inner: &str) -> &mut Self;
    fn ipv4(&mut self) -> &mut Self;
    fn ipv6(&mut self) -> &mut Self;
    fn map(&mut self, key_type: &str, value_type: &str) -> &mut Self;
    fn tuple(&mut self, element_types: &[&str]) -> &mut Self;
    fn enum8(&mut self, variants: &[(&str, i8)]) -> &mut Self;
    fn enum16(&mut self, variants: &[(&str, i16)]) -> &mut Self;
    fn simple_aggregate_function(&mut self, func: &str, inner_type: &str) -> &mut Self;
    fn aggregate_function(&mut self, func: &str, inner_type: &str) -> &mut Self;
    fn nothing(&mut self) -> &mut Self;
}

impl ClickhouseColumnDefExt for ColumnDef {
    fn int128(&mut self) -> &mut Self {
        self.custom(Alias::new("Int128"))
    }
    fn int256(&mut self) -> &mut Self {
        self.custom(Alias::new("Int256"))
    }
    fn uint128(&mut self) -> &mut Self {
        self.custom(Alias::new("UInt128"))
    }
    fn uint256(&mut self) -> &mut Self {
        self.custom(Alias::new("UInt256"))
    }
    fn float32(&mut self) -> &mut Self {
        self.custom(Alias::new("Float32"))
    }
    fn float64(&mut self) -> &mut Self {
        self.custom(Alias::new("Float64"))
    }
    fn decimal32(&mut self, scale: u32) -> &mut Self {
        self.custom(Alias::new(format!("Decimal32({scale})")))
    }
    fn decimal64(&mut self, scale: u32) -> &mut Self {
        self.custom(Alias::new(format!("Decimal64({scale})")))
    }
    fn decimal128(&mut self, scale: u32) -> &mut Self {
        self.custom(Alias::new(format!("Decimal128({scale})")))
    }
    fn decimal256(&mut self, scale: u32) -> &mut Self {
        self.custom(Alias::new(format!("Decimal256({scale})")))
    }
    fn date32(&mut self) -> &mut Self {
        self.custom(Alias::new("Date32"))
    }
    fn date_time64(&mut self, precision: u32) -> &mut Self {
        self.custom(Alias::new(format!("DateTime64({precision})")))
    }
    fn date_time64_tz(&mut self, precision: u32, timezone: &str) -> &mut Self {
        self.custom(Alias::new(format!("DateTime64({precision}, '{timezone}')")))
    }
    fn date_time_tz(&mut self, timezone: &str) -> &mut Self {
        self.custom(Alias::new(format!("DateTime('{timezone}')")))
    }
    fn fixed_string(&mut self, length: u32) -> &mut Self {
        self.custom(Alias::new(format!("FixedString({length})")))
    }
    fn low_cardinality(&mut self, inner: &str) -> &mut Self {
        self.custom(Alias::new(format!("LowCardinality({inner})")))
    }
    fn clickhouse_nullable(&mut self, inner: &str) -> &mut Self {
        self.custom(Alias::new(format!("Nullable({inner})")))
    }
    fn ipv4(&mut self) -> &mut Self {
        self.custom(Alias::new("IPv4"))
    }
    fn ipv6(&mut self) -> &mut Self {
        self.custom(Alias::new("IPv6"))
    }
    fn map(&mut self, key_type: &str, value_type: &str) -> &mut Self {
        self.custom(Alias::new(format!("Map({key_type}, {value_type})")))
    }
    fn tuple(&mut self, element_types: &[&str]) -> &mut Self {
        self.custom(Alias::new(format!("Tuple({})", element_types.join(", "))))
    }
    fn enum8(&mut self, variants: &[(&str, i8)]) -> &mut Self {
        let inner = variants
            .iter()
            .map(|(name, val)| format!("'{name}' = {val}"))
            .collect::<Vec<_>>()
            .join(", ");
        self.custom(Alias::new(format!("Enum8({inner})")))
    }
    fn enum16(&mut self, variants: &[(&str, i16)]) -> &mut Self {
        let inner = variants
            .iter()
            .map(|(name, val)| format!("'{name}' = {val}"))
            .collect::<Vec<_>>()
            .join(", ");
        self.custom(Alias::new(format!("Enum16({inner})")))
    }
    fn simple_aggregate_function(&mut self, func: &str, inner_type: &str) -> &mut Self {
        self.custom(Alias::new(format!(
            "SimpleAggregateFunction({func}, {inner_type})"
        )))
    }
    fn aggregate_function(&mut self, func: &str, inner_type: &str) -> &mut Self {
        self.custom(Alias::new(format!(
            "AggregateFunction({func}, {inner_type})"
        )))
    }
    fn nothing(&mut self) -> &mut Self {
        self.custom(Alias::new("Nothing"))
    }
}
