use pretty_assertions::assert_eq;
use sea_query::{tests_cfg::*, *};
use sea_query_clickhouse::*;

#[test]
fn create_1() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new(Glyph::Id).integer().not_null())
                .col(ColumnDef::new(Glyph::Aspect).double().not_null())
                .col(ColumnDef::new(Glyph::Image).text())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by([Glyph::Id])
        .to_string(),
        [
            r#"CREATE TABLE `glyph`"#,
            r#"(`id` Int32 NOT NULL, `aspect` Float64 NOT NULL, `image` String)"#,
            r#"ENGINE = MergeTree()"#,
            r#"ORDER BY (`id`)"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_2() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Font::Table)
                .col(ColumnDef::new(Font::Id).integer().not_null())
                .col(ColumnDef::new(Font::Name).string().not_null())
                .col(ColumnDef::new(Font::Variant).string_len(255).not_null())
                .col(ColumnDef::new(Font::Language).string_len(255).not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by([Font::Id])
        .to_string(),
        [
            r#"CREATE TABLE `font`"#,
            r#"(`id` Int32 NOT NULL, `name` String NOT NULL, `variant` String NOT NULL, `language` String NOT NULL)"#,
            r#"ENGINE = MergeTree()"#,
            r#"ORDER BY (`id`)"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_if_not_exists() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .if_not_exists()
                .col(ColumnDef::new(Glyph::Id).integer().not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by([Glyph::Id])
        .to_string(),
        [
            r#"CREATE TABLE IF NOT EXISTS `glyph`"#,
            r#"(`id` Int32 NOT NULL)"#,
            r#"ENGINE = MergeTree()"#,
            r#"ORDER BY (`id`)"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_with_engine_params() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new(Glyph::Id).integer().not_null())
                .col(ColumnDef::new(Glyph::Aspect).double().not_null())
                .to_owned()
        )
        .engine_with_params("ReplacingMergeTree", vec!["ver".to_string()])
        .order_by([Glyph::Id])
        .to_string(),
        [
            r#"CREATE TABLE `glyph`"#,
            r#"(`id` Int32 NOT NULL, `aspect` Float64 NOT NULL)"#,
            r#"ENGINE = ReplacingMergeTree(ver)"#,
            r#"ORDER BY (`id`)"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_with_partition_by() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new(Glyph::Id).integer().not_null())
                .col(ColumnDef::new(Glyph::Aspect).double().not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by([Glyph::Id])
        .partition_by_expr(Expr::cust("toYYYYMM(created_at)"))
        .to_string(),
        [
            r#"CREATE TABLE `glyph`"#,
            r#"(`id` Int32 NOT NULL, `aspect` Float64 NOT NULL)"#,
            r#"ENGINE = MergeTree()"#,
            r#"ORDER BY (`id`)"#,
            r#"PARTITION BY toYYYYMM(created_at)"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_with_primary_key() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new(Glyph::Id).integer().not_null())
                .col(ColumnDef::new(Glyph::Aspect).double().not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by([Glyph::Id, Glyph::Aspect])
        .primary_key([Glyph::Id])
        .to_string(),
        [
            r#"CREATE TABLE `glyph`"#,
            r#"(`id` Int32 NOT NULL, `aspect` Float64 NOT NULL)"#,
            r#"ENGINE = MergeTree()"#,
            r#"ORDER BY (`id`, `aspect`)"#,
            r#"PRIMARY KEY (`id`)"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_with_ttl() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new(Glyph::Id).integer().not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by([Glyph::Id])
        .ttl("created_at + INTERVAL 1 MONTH")
        .to_string(),
        [
            r#"CREATE TABLE `glyph`"#,
            r#"(`id` Int32 NOT NULL)"#,
            r#"ENGINE = MergeTree()"#,
            r#"ORDER BY (`id`)"#,
            r#"TTL created_at + INTERVAL 1 MONTH"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_with_settings() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new(Glyph::Id).integer().not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by([Glyph::Id])
        .table_setting("index_granularity", "8192")
        .to_string(),
        [
            r#"CREATE TABLE `glyph`"#,
            r#"(`id` Int32 NOT NULL)"#,
            r#"ENGINE = MergeTree()"#,
            r#"ORDER BY (`id`)"#,
            r#"SETTINGS index_granularity = 8192"#,
        ]
        .join(" ")
    );
}

#[test]
fn create_column_types() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("col_i8").tiny_integer())
                .col(ColumnDef::new("col_u8").tiny_unsigned())
                .col(ColumnDef::new("col_i16").small_integer())
                .col(ColumnDef::new("col_u16").small_unsigned())
                .col(ColumnDef::new("col_i32").integer())
                .col(ColumnDef::new("col_u32").unsigned())
                .col(ColumnDef::new("col_i64").big_integer())
                .col(ColumnDef::new("col_u64").big_unsigned())
                .col(ColumnDef::new("col_f32").float())
                .col(ColumnDef::new("col_f64").double())
                .col(ColumnDef::new("col_str").string())
                .col(ColumnDef::new("col_bool").boolean())
                .col(ColumnDef::new("col_date").date())
                .col(ColumnDef::new("col_dt").date_time())
                .col(ColumnDef::new("col_uuid").uuid())
                .col(ColumnDef::new("col_dec").decimal_len(10, 2))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["col_i32"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`col_i8` Int8, `col_u8` UInt8, `col_i16` Int16, `col_u16` UInt16, `col_i32` Int32, `col_u32` UInt32, `col_i64` Int64, `col_u64` UInt64, `col_f32` Float32, `col_f64` Float64, `col_str` String, `col_bool` Bool, `col_date` Date, `col_dt` DateTime, `col_uuid` UUID, `col_dec` Decimal(10, 2)) ENGINE = MergeTree() ORDER BY (`col_i32`)"#
    );
}

#[test]
fn drop_table() {
    assert_eq!(
        Table::drop()
            .table(Glyph::Table)
            .to_string(ClickhouseQueryBuilder),
        r#"DROP TABLE `glyph`"#
    );
}

#[test]
fn drop_table_if_exists() {
    assert_eq!(
        Table::drop()
            .table(Glyph::Table)
            .if_exists()
            .to_string(ClickhouseQueryBuilder),
        r#"DROP TABLE IF EXISTS `glyph`"#
    );
}

#[test]
fn alter_table_add_column() {
    // NOTE: ALTER TABLE delegates to MySQL builder for the statement structure.
    // Column types are rendered using MySQL's type mapping. For proper ClickHouse types
    // in ALTER TABLE, use ColumnDef::new("col").custom(Alias::new("Int32")).
    assert_eq!(
        Table::alter()
            .table(Font::Table)
            .add_column(
                ColumnDef::new("new_col")
                    .custom(Alias::new("Int32"))
                    .not_null()
            )
            .to_string(ClickhouseQueryBuilder),
        r#"ALTER TABLE `font` ADD COLUMN `new_col` Int32 NOT NULL"#
    );
}

#[test]
fn alter_table_drop_column() {
    assert_eq!(
        Table::alter()
            .table(Font::Table)
            .drop_column("old_col")
            .to_string(ClickhouseQueryBuilder),
        r#"ALTER TABLE `font` DROP COLUMN `old_col`"#
    );
}

#[test]
fn alter_table_rename_column() {
    assert_eq!(
        Table::alter()
            .table(Font::Table)
            .rename_column("old_col", "new_col")
            .to_string(ClickhouseQueryBuilder),
        r#"ALTER TABLE `font` RENAME COLUMN `old_col` TO `new_col`"#
    );
}

#[test]
fn rename_table() {
    assert_eq!(
        Table::rename()
            .table(Font::Table, "new_font")
            .to_string(ClickhouseQueryBuilder),
        r#"RENAME TABLE `font` TO `new_font`"#
    );
}

#[test]
fn truncate_table() {
    assert_eq!(
        Table::truncate()
            .table(Glyph::Table)
            .to_string(ClickhouseQueryBuilder),
        r#"TRUNCATE TABLE `glyph`"#
    );
}

// ClickHouse-specific data type tests

#[test]
fn create_with_int128() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("big_val").int128().not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["big_val"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`big_val` Int128 NOT NULL) ENGINE = MergeTree() ORDER BY (`big_val`)"#
    );
}

#[test]
fn create_with_uint256() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("huge_val").uint256())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["huge_val"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`huge_val` UInt256) ENGINE = MergeTree() ORDER BY (`huge_val`)"#
    );
}

#[test]
fn create_with_datetime64() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("created_at").date_time64(3).not_null())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["created_at"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`created_at` DateTime64(3) NOT NULL) ENGINE = MergeTree() ORDER BY (`created_at`)"#
    );
}

#[test]
fn create_with_datetime64_tz() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("ts").date_time64_tz(6, "UTC"))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["ts"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`ts` DateTime64(6, 'UTC')) ENGINE = MergeTree() ORDER BY (`ts`)"#
    );
}

#[test]
fn create_with_datetime_tz() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("ts").date_time_tz("America/New_York"))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["ts"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`ts` DateTime('America/New_York')) ENGINE = MergeTree() ORDER BY (`ts`)"#
    );
}

#[test]
fn create_with_date32() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("d").date32())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["d"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`d` Date32) ENGINE = MergeTree() ORDER BY (`d`)"#
    );
}

#[test]
fn create_with_low_cardinality() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("status").low_cardinality("String"))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["status"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`status` LowCardinality(String)) ENGINE = MergeTree() ORDER BY (`status`)"#
    );
}

#[test]
fn create_with_ipv4() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("ip").ipv4())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["ip"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`ip` IPv4) ENGINE = MergeTree() ORDER BY (`ip`)"#
    );
}

#[test]
fn create_with_ipv6() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("ip").ipv6())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["ip"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`ip` IPv6) ENGINE = MergeTree() ORDER BY (`ip`)"#
    );
}

#[test]
fn create_with_map() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("metadata").map("String", "String"))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["metadata"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`metadata` Map(String, String)) ENGINE = MergeTree() ORDER BY (`metadata`)"#
    );
}

#[test]
fn create_with_tuple() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("point").tuple(&["Float64", "Float64"]))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["point"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`point` Tuple(Float64, Float64)) ENGINE = MergeTree() ORDER BY (`point`)"#
    );
}

#[test]
fn create_with_enum8() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("status").enum8(&[("active", 1), ("inactive", 2)]))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["status"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`status` Enum8('active' = 1, 'inactive' = 2)) ENGINE = MergeTree() ORDER BY (`status`)"#
    );
}

#[test]
fn create_with_nullable_type() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("opt_val").clickhouse_nullable("UInt32"))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["opt_val"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`opt_val` Nullable(UInt32)) ENGINE = MergeTree() ORDER BY (`opt_val`)"#
    );
}

#[test]
fn create_with_decimal_variants() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("d32").decimal32(2))
                .col(ColumnDef::new("d64").decimal64(4))
                .col(ColumnDef::new("d128").decimal128(6))
                .col(ColumnDef::new("d256").decimal256(8))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["d32"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`d32` Decimal32(2), `d64` Decimal64(4), `d128` Decimal128(6), `d256` Decimal256(8)) ENGINE = MergeTree() ORDER BY (`d32`)"#
    );
}

#[test]
fn create_with_aggregate_function() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("cnt").simple_aggregate_function("sum", "UInt64"))
                .to_owned()
        )
        .engine("AggregatingMergeTree")
        .order_by(["cnt"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`cnt` SimpleAggregateFunction(sum, UInt64)) ENGINE = AggregatingMergeTree() ORDER BY (`cnt`)"#
    );
}

#[test]
fn create_with_nothing() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("placeholder").nothing())
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["placeholder"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`placeholder` Nothing) ENGINE = MergeTree() ORDER BY (`placeholder`)"#
    );
}

#[test]
fn create_with_fixed_string() {
    assert_eq!(
        ClickhouseTableCreate::new(
            Table::create()
                .table(Glyph::Table)
                .col(ColumnDef::new("code").fixed_string(3))
                .to_owned()
        )
        .engine("MergeTree")
        .order_by(["code"])
        .to_string(),
        r#"CREATE TABLE `glyph` (`code` FixedString(3)) ENGINE = MergeTree() ORDER BY (`code`)"#
    );
}
