use tici_grpc::{
    CreateIndexRequest, HybridIndexInfo, HybridInverted, HybridSharding, HybridSort, tidb_def,
    tidb_types::{
        CIStr, ColumnInfo, FieldType, IndexColumn, IndexInfo, IndexType, PartitionDefinition,
        PartitionTableInfo, PartitionType, TableInfo,
    },
};

fn cistr(name: &str) -> CIStr {
    CIStr {
        original: name.to_string(),
        lower_case: name.to_string(),
    }
}

fn make_field_type(tp: u8, flag: u32, flen: i32) -> FieldType {
    FieldType {
        tp,
        flag,
        flen,
        decimal: 0,
        charset: "utf8mb4".to_string(),
        collate: "utf8mb4_bin".to_string(),
        elems: None,
        elems_is_binary_lit: None,
        array: false,
    }
}

fn make_column(id: i64, name: &str, tp: u8, flag: u32, offset: i32) -> ColumnInfo {
    ColumnInfo {
        id,
        name: cistr(name),
        offset,
        field_type: make_field_type(tp, flag, 255),
    }
}

fn make_index_column(name: &str, offset: i32) -> IndexColumn {
    IndexColumn {
        name: cistr(name),
        offset,
        length: -1,
    }
}

pub fn make_create_partition_index_req(
    index_key: &tici_common::index::IndexKey,
    physical_table_ids: &[i64],
) -> CreateIndexRequest {
    let columns = vec![
        make_column(
            1,
            "id",
            tidb_def::COLTYPE_LONG as u8,
            tidb_def::PRI_KEY_FLAG,
            0,
        ),
        make_column(2, "timestamp", tidb_def::COLTYPE_TIMESTAMP as u8, 0, 1),
        make_column(3, "severity_text", tidb_def::COLTYPE_VARCHAR as u8, 0, 2),
        make_column(4, "body", tidb_def::COLTYPE_VARCHAR as u8, 0, 3),
        make_column(
            5,
            "tenant_id",
            tidb_def::COLTYPE_LONG as u8,
            tidb_def::PRI_KEY_FLAG,
            4,
        ),
    ];

    let hybrid_index = HybridIndexInfo {
        inverted: Some(vec![HybridInverted {
            columns: vec![
                make_index_column("severity_text", 2),
                make_index_column("body", 3),
                make_index_column("id", 0),
                make_index_column("tenant_id", 4),
            ],
        }]),
        sort: Some(HybridSort {
            columns: vec![
                make_index_column("severity_text", 2),
                make_index_column("body", 3),
                make_index_column("id", 0),
                make_index_column("tenant_id", 4),
            ],
            is_asc: vec![true, true, true, true],
        }),
        sharding_key: Some(HybridSharding {
            columns: vec![make_index_column("tenant_id", 4)],
        }),
    };
    let index_info = IndexInfo {
        id: index_key.index_id,
        name: cistr("fulltext_severity_body_idx"),
        table: Some(cistr("partition_table")),
        columns: vec![
            make_index_column("severity_text", 2),
            make_index_column("body", 3),
            make_index_column("id", 0),
            make_index_column("tenant_id", 4),
        ],
        tp: IndexType::Hybrid,
        unique: false,
        primary: false,
        invisible: false,
        global: false,
        mv_index: false,
        full_text_info: None,
        hybrid_index: Some(hybrid_index),
    };

    let partition_defs = physical_table_ids
        .iter()
        .enumerate()
        .map(|(idx, id)| PartitionDefinition {
            id: *id,
            name: cistr(&format!("p{}", idx)),
            less_than: vec![if idx + 1 == physical_table_ids.len() {
                "MAXVALUE".to_string()
            } else {
                format!("{}", (idx + 1) * 10)
            }],
            in_values: None,
            policy_ref_info: None,
        })
        .collect::<Vec<_>>();

    let partition_info = PartitionTableInfo {
        partition_type: PartitionType::Range,
        expr: Some("`id`".to_string()),
        enable: true,
        is_empty_columns: false,
        definitions: partition_defs.clone(),
        num: partition_defs.len() as u64,
    };

    let table_info = TableInfo {
        id: index_key.table_id,
        name: cistr("partition_table"),
        charset: "utf8mb4".to_string(),
        collate: "utf8mb4_bin".to_string(),
        columns,
        indexes: vec![index_info],
        pk_is_handle: false,
        is_common_handle: true,
        update_ts: 0,
        version: 1,
        partition: Some(partition_info),
    };

    // Transform table_info to json string
    let json_string = serde_json::to_string(&table_info).unwrap();
    CreateIndexRequest {
        database_name: "test".to_string(),
        table_info: json_string.into_bytes(),
        index_id: index_key.index_id,
        keyspace_id: index_key.keyspace_id,
        parser_info: Some(tici_grpc::DEFAULT_PARSER_INFO.clone()),
    }
}
