package org.springblade.datasource.transform.mysql;

import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.exception.ServiceException;
import org.springblade.datasource.support.DynamicDataSourceSupport;
import org.springblade.datasource.transform.*;
import org.springblade.datasource.transform.util.ListUtils;
import org.springblade.etl.entity.bi.ReportDatasource;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 喜欢番茄火锅
 * @date 2023/8/18 17:42
 */
@Slf4j
public class MysqlCreateTableHandle {


    /**
     * 根据通用表信息创建表对象
     *
     * @param tableInfo
     * @return
     * @author 王瑞
     * @date 2023/8/16
     */
    @SneakyThrows
    public void creatTable(TableInfo tableInfo, ReportDatasource datasource, boolean deleteSourceTable) {

        String columSql = buildColumnSql(tableInfo.getTableName(), tableInfo.getTableComment(), tableInfo.getColumnInfo());
        log.info("创建表sql 语句,sql:{}", columSql);
        inspectIndex(tableInfo);
        List<String> indexSql = buildIndexSql(tableInfo.getTableName(), tableInfo.getIndexInfo());
        log.info("创建表索引sql 语句,sql:{}", indexSql);
        Connection connection = null;
        connection = DynamicDataSourceSupport.createConnection(datasource);
        Statement statement = connection.createStatement();
        ResultSet tables = connection.getMetaData().getTables(null, datasource.getDsDb(), tableInfo.getTableName(), new String[]{"TABLE", "VIEW"});
        if (tables.next()) {
            List<ColumnInfo> sourceColumn = new MysqlParseTableHandle().getSourceColumn(tableInfo.getTableName(), connection.getMetaData(), datasource.getDbSchema());
            List<String> collect = tableInfo.getColumnInfo().stream().map(ColumnInfo::getColumnName).collect(Collectors.toList());
            List<String> collect2 = sourceColumn.stream().map(ColumnInfo::getColumnName).collect(Collectors.toList());
            if (!ListUtils.equalLists(collect, collect2)) {
                throw new ServiceException("mysql 源,表名称:" + tableInfo.getTableName() + "已存在且字段不符!");
            }
            return;
        }

        statement.executeUpdate(columSql);
        for (String s : indexSql) {
            statement.executeUpdate(s);
        }
        statement.close();

    }


    private void inspectIndex(TableInfo tableInfo) {
        List<ColumnInfo> columnInfo = tableInfo.getColumnInfo();
        List<IndexInfo> indexInfoList = tableInfo.getIndexInfo();
        if (CollectionUtil.isEmpty(columnInfo) || CollectionUtil.isEmpty(indexInfoList)) {
            return;
        }

        Iterator<IndexInfo> iterator = indexInfoList.iterator();
        while (iterator.hasNext()) {
            IndexInfo indexInfo = iterator.next();

            for (IndexInfo.IndexColumn next : indexInfo.getIndexColumn()) {
                boolean flag = columnInfo.stream().anyMatch(columnName -> {
                    //判断现有的name是否存在集合中
                    return columnName.getColumnName().equalsIgnoreCase(next.getColumnName());
                });
                if (!flag) {
                    iterator.remove();
                    break;
                }
            }

        }
    }

    private String buildColumnSql(String tableName, String tableComment, List<ColumnInfo> columnInfos) {
        StringBuilder fieldSql = new StringBuilder();
        columnInfos.stream().forEach(columnInfo -> fieldSql.append(String.format(
                "%s %s comment '%s',",
                "`"+columnInfo.getColumnName()+"`",
                getColumnType(columnInfo),
                columnInfo.getColumnComments()
        )));
        fieldSql.deleteCharAt(fieldSql.length() - 1);
        return String.format("CREATE TABLE %s (%s) COMMENT '%s' ;", "`"+tableName+"`", fieldSql, tableComment);
    }

    private List<String> buildIndexSql(String tableName, List<IndexInfo> indexInfos) {
        List<String> list = Lists.newArrayList();

        if (CollectionUtil.isEmpty(indexInfos)) {
            return list;
        }
        indexInfos.stream().forEach(indexInfo -> {
                    StringBuilder indexSql = new StringBuilder();
                    indexSql.append(String.format(
                            "ALTER TABLE %s ADD %s %s ( %s ) ;",
                            tableName,
                            getIndexType(indexInfo),
                            indexInfo.getPrimaryKey() ? "" : indexInfo.getIndexName(),
                            getIndexColumn(indexInfo.getIndexColumn())));
                    list.add(indexSql.toString());
                }

        );
        return list;
    }

    private String getIndexColumn(List<IndexInfo.IndexColumn> list) {
        StringBuilder sb = new StringBuilder();
        list.stream().sorted((Comparator.comparing(IndexInfo.IndexColumn::getOrdinalPosition))).forEach(v -> {
            sb.append("`"+v.getColumnName() + "`,");
        });
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }

    private String getIndexType(IndexInfo indexInfo) {
        StringBuilder sb = new StringBuilder();
        if (indexInfo.getPrimaryKey()) {
            return " PRIMARY KEY ";
        }
        if (!indexInfo.getNonUnique()) {
            sb.append(" UNIQUE ");
        }
        sb.append(" INDEX ");
        return sb.toString();
    }

    public String getColumnType(ColumnInfo columnInfo) {
        ColumnEnum columnEnum = columnInfo.getColumnType();
        Integer columnSize = columnInfo.getColumnSize();
        Integer decimalDigits = columnInfo.getDecimalDigits();

        switch (columnEnum) {
            case DECIMAL:
                Integer p = TransformUtil.getColumnSizeOrDecimalDigits(columnSize, 30, 65, 1);
                Integer pd = 30;
                if (p < 30) {
                    pd = p;
                }
                Integer d = TransformUtil.getColumnSizeOrDecimalDigits(decimalDigits, 4, pd, 0);
                return String.format("decimal(%s,%s)", p, d);

            case STRING:
                Integer string = TransformUtil.getColumnSizeOrDecimalDigits(columnSize, 255, 255, 1);
                if (columnSize != null && columnSize > 255) {
                    return "text";
                }
                if (columnSize != null && columnSize < 1) {
                    return "text";
                }
                return "varchar(" + string + ")";
            case LONG:
                return "bigint(20)";
            case INTEGER:
                return "int(11)";
            case DOUBLE:
                return "double";
            case FLOAT:
                return "float";
            case DATE:
                return "datetime";
            case BOOL:
                Integer bool = TransformUtil.getColumnSizeOrDecimalDigits(columnSize, 1, 64, 1);
                return "bit(" + bool + ")";
            default:
                return "";
        }

    }
}
