use sea_query::*;
use std::fmt::Write;

use crate::ClickhouseQueryBuilder;

fn write_int(w: &mut (impl Write + ?Sized), n: impl std::fmt::Display) {
    write!(w, "{n}").unwrap();
}

impl TableBuilder for ClickhouseQueryBuilder {
    fn prepare_column_def(&self, column_def: &ColumnDef, sql: &mut impl SqlWriter) {
        let name = column_def.get_column_name();
        self.prepare_iden(&Alias::new(&name).into_iden(), sql);

        if let Some(column_type) = column_def.get_column_type() {
            sql.write_str(" ").unwrap();
            self.prepare_column_type(column_type, sql);
        }

        self.prepare_column_spec(column_def.get_column_spec(), sql);
    }

    fn prepare_column_type(&self, column_type: &ColumnType, sql: &mut impl SqlWriter) {
        match column_type {
            ColumnType::Char(Some(length)) => {
                sql.write_str("FixedString(").unwrap();
                write_int(sql, *length);
                sql.write_char(')')
            }
            ColumnType::Char(None) => sql.write_str("String"),
            ColumnType::String(_) | ColumnType::Text => sql.write_str("String"),
            ColumnType::TinyInteger => sql.write_str("Int8"),
            ColumnType::TinyUnsigned => sql.write_str("UInt8"),
            ColumnType::SmallInteger => sql.write_str("Int16"),
            ColumnType::SmallUnsigned => sql.write_str("UInt16"),
            ColumnType::Integer => sql.write_str("Int32"),
            ColumnType::Unsigned => sql.write_str("UInt32"),
            ColumnType::BigInteger => sql.write_str("Int64"),
            ColumnType::BigUnsigned => sql.write_str("UInt64"),
            ColumnType::Float => sql.write_str("Float32"),
            ColumnType::Double => sql.write_str("Float64"),
            ColumnType::Decimal(precision) => match precision {
                Some((precision, scale)) => {
                    sql.write_str("Decimal(").unwrap();
                    write_int(sql, *precision);
                    sql.write_str(", ").unwrap();
                    write_int(sql, *scale);
                    sql.write_char(')')
                }
                None => sql.write_str("Decimal"),
            },
            ColumnType::DateTime => sql.write_str("DateTime"),
            ColumnType::Timestamp => sql.write_str("DateTime"),
            ColumnType::TimestampWithTimeZone => sql.write_str("DateTime"),
            ColumnType::Time => sql.write_str("String"),
            ColumnType::Date => sql.write_str("Date"),
            ColumnType::Interval(_, _) => sql.write_str("Int64"),
            ColumnType::Binary(_) | ColumnType::VarBinary(_) | ColumnType::Blob => {
                sql.write_str("String")
            }
            ColumnType::Bit(_) => sql.write_str("UInt64"),
            ColumnType::VarBit(_) => sql.write_str("UInt64"),
            ColumnType::Boolean => sql.write_str("Bool"),
            ColumnType::Money(_) => sql.write_str("Decimal(19, 4)"),
            ColumnType::Json | ColumnType::JsonBinary => sql.write_str("String"),
            ColumnType::Uuid => sql.write_str("UUID"),
            ColumnType::Array(elem_type) => {
                sql.write_str("Array(").unwrap();
                self.prepare_column_type(elem_type, sql);
                sql.write_str(")")
            }
            ColumnType::Vector(size) => {
                let _ = size;
                sql.write_str("Array(Float32)")
            }
            ColumnType::Custom(iden) => sql.write_str(&iden.to_string()),
            ColumnType::Enum { name, .. } => sql.write_str(&name.to_string()),
            ColumnType::Cidr | ColumnType::Inet => sql.write_str("IPv6"),
            ColumnType::MacAddr => sql.write_str("String"),
            ColumnType::Year => sql.write_str("UInt16"),
            ColumnType::LTree => sql.write_str("String"),
            _ => sql.write_str("String"),
        }
        .unwrap()
    }

    fn column_spec_auto_increment_keyword(&self) -> &str {
        ""
    }

    fn prepare_table_opt(&self, _create: &TableCreateStatement, _sql: &mut impl SqlWriter) {}

    fn prepare_table_alter_statement(&self, alter: &TableAlterStatement, sql: &mut impl SqlWriter) {
        MysqlQueryBuilder.prepare_table_alter_statement(alter, sql);
    }

    fn prepare_table_rename_statement(
        &self,
        rename: &TableRenameStatement,
        sql: &mut impl SqlWriter,
    ) {
        MysqlQueryBuilder.prepare_table_rename_statement(rename, sql);
    }
}
