use sea_query::*;

use crate::ClickhouseQueryBuilder;

impl QueryBuilder for ClickhouseQueryBuilder {
    fn prepare_select_into(&self, _into_table: &SelectInto, _sql: &mut impl SqlWriter) {}

    fn prepare_function_name(&self, function: &Func, sql: &mut impl SqlWriter) {
        match function {
            // ClickHouse uses `groupBitAnd` / `groupBitOr` instead of MySQL's `BIT_AND` / `BIT_OR`
            Func::BitAnd => sql.write_str("groupBitAnd").unwrap(),
            Func::BitOr => sql.write_str("groupBitOr").unwrap(),
            _ => self.prepare_function_name_common(function, sql),
        }
    }

    fn prepare_bin_oper(&self, bin_oper: &BinOper, sql: &mut impl SqlWriter) {
        self.prepare_bin_oper_common(bin_oper, sql);
    }

    fn prepare_query_statement(&self, query: &SubQueryStatement, sql: &mut impl SqlWriter) {
        MysqlQueryBuilder.prepare_query_statement(query, sql);
    }

    fn prepare_value(&self, value: Value, sql: &mut impl SqlWriter) {
        sql.push_param(value, self as _);
    }

    fn prepare_returning(&self, _returning: &Option<ReturningClause>, _sql: &mut impl SqlWriter) {}

    fn prepare_on_conflict(&self, _on_conflict: &Option<OnConflict>, _sql: &mut impl SqlWriter) {}

    fn if_null_function(&self) -> &str {
        "ifNull"
    }

    fn random_function(&self) -> &str {
        "rand"
    }

    /// ClickHouse mutations use `ALTER TABLE ... DELETE WHERE ...` syntax
    /// instead of standard `DELETE FROM ... WHERE ...`.
    ///
    /// See: <https://clickhouse.com/docs/en/sql-reference/statements/alter/delete>
    fn prepare_delete_statement(&self, delete: &DeleteStatement, sql: &mut impl SqlWriter) {
        let mysql_sql = delete.to_string(MysqlQueryBuilder);
        // Transform: "DELETE FROM `table` WHERE ..." -> "ALTER TABLE `table` DELETE WHERE ..."
        if let Some(rest) = mysql_sql.strip_prefix("DELETE FROM ") {
            if let Some(pos) = rest.find(" WHERE ") {
                let table_part = &rest[..pos];
                // Skip the leading space before WHERE so we get "WHERE ..."
                let where_part = &rest[pos + 1..];
                sql.write_str("ALTER TABLE ").unwrap();
                sql.write_str(table_part).unwrap();
                sql.write_str(" DELETE ").unwrap();
                sql.write_str(where_part).unwrap();
            } else {
                // No WHERE clause: "DELETE FROM `table`" -> "ALTER TABLE `table` DELETE"
                sql.write_str("ALTER TABLE ").unwrap();
                sql.write_str(rest).unwrap();
                sql.write_str(" DELETE").unwrap();
            }
        } else {
            // Fallback for unexpected format
            sql.write_str(&mysql_sql).unwrap();
        }
    }

    /// ClickHouse mutations use `ALTER TABLE ... UPDATE ... SET ... WHERE ...` syntax
    /// instead of standard `UPDATE ... SET ... WHERE ...`.
    ///
    /// See: <https://clickhouse.com/docs/en/sql-reference/statements/alter/update>
    fn prepare_update_statement(&self, update: &UpdateStatement, sql: &mut impl SqlWriter) {
        let mysql_sql = update.to_string(MysqlQueryBuilder);
        // Transform: "UPDATE `table` SET col = val WHERE ..." -> "ALTER TABLE `table` UPDATE col = val WHERE ..."
        if let Some(rest) = mysql_sql.strip_prefix("UPDATE ") {
            if let Some(pos) = rest.find(" SET ") {
                let table_part = &rest[..pos];
                // Skip " SET " (5 chars) to get the assignments and optional WHERE
                let assignments_part = &rest[pos + 5..];
                sql.write_str("ALTER TABLE ").unwrap();
                sql.write_str(table_part).unwrap();
                sql.write_str(" UPDATE ").unwrap();
                sql.write_str(assignments_part).unwrap();
            } else {
                // Fallback for unexpected format (no SET clause)
                sql.write_str(&mysql_sql).unwrap();
            }
        } else {
            // Fallback for unexpected format
            sql.write_str(&mysql_sql).unwrap();
        }
    }

    fn prepare_explain_statement(&self, explain: &ExplainStatement, sql: &mut impl SqlWriter) {
        MysqlQueryBuilder.prepare_explain_statement(explain, sql);
    }
}
