package org.springblade.datasource.transform;

import cn.hutool.core.map.CaseInsensitiveMap;
import com.alibaba.druid.util.JdbcUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springblade.datasource.support.DynamicDataSourceSupport;
import org.springblade.datasource.transform.general.GeneralModel;
import org.springblade.datasource.transform.general.GeneralParseTableHandle;
import org.springblade.datasource.transform.mysql.SQLColumnTypeEnum;
import org.springblade.datasource.transform.util.ListUtils;
import org.springblade.etl.entity.bi.ReportDatasource;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 喜欢番茄火锅
 * @date 2023/12/25 15:37
 */
@Slf4j
public abstract class JdbcParseTableAbstract {

    /**
     * 将源信息转换成通用表数据
     *
     * @param datasource
     * @return {@link TableInfo}
     * @author 王瑞
     * @date 2023/8/16
     */
    public TableInfo parseTableInfo(ReportDatasource datasource, List<GeneralModel> model, String tableViewName, String tableComment) {
        if (datasource == null) {
            return null;
        }
        Connection connection = null;
        try {
            connection = DynamicDataSourceSupport.createConnection(datasource);
            DatabaseMetaData metaData = connection.getMetaData();
            String schemaPattern = datasource.getDbSchema();

            TableInfo tableInfo = new TableInfo();
            tableInfo.setTableName(tableViewName);

            if (StringUtils.isNotBlank(tableComment)) {
                tableInfo.setTableComment(tableComment);
            } else {
                getTableComment(metaData, schemaPattern, tableViewName, tableInfo);
            }

            tableInfo.setColumnInfo(getColumnInfo(tableViewName, metaData, schemaPattern, model));
            tableInfo.setIndexInfo(getPrimaryKeys(tableViewName, metaData, schemaPattern));

            return tableInfo;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            JdbcUtils.close(connection);
        }
        return null;
    }

    public TableInfo getTableComment(DatabaseMetaData metaData, String schema, String tableViewName, TableInfo tableInfo) {
        try {
            ResultSet rs = metaData.getTables(null, schema, tableViewName, new String[]{"TABLE", "VIEW"});
            while (rs.next()) {
                tableInfo.setTableComment(rs.getString("REMARKS"));
            }
            rs.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return tableInfo;
    }

    private List<IndexInfo> getPrimaryKeys(String tableViewName, DatabaseMetaData metaData, String schemaPattern) throws SQLException {
        ResultSet rs = null;
        HashMap<String, IndexInfo> map = new HashMap<>();

        rs = metaData.getPrimaryKeys(null, schemaPattern, tableViewName);
        while (rs.next()) {

            String indexName = rs.getString("pk_name");

            IndexInfo indexInfo = map.get(indexName);
            if (indexInfo != null) {
                List<IndexInfo.IndexColumn> indexColumn = indexInfo.getIndexColumn();
                indexColumn(rs, indexInfo, indexColumn);
                continue;
            }
            indexInfo = new IndexInfo();
            indexInfo.setPrimaryKey(true);
            indexInfo.setIndexName(indexName);

            ArrayList<IndexInfo.IndexColumn> indexColumns = Lists.newArrayList();
            indexColumn(rs, indexInfo, indexColumns);
            indexInfo.setIndexColumn(indexColumns);
            map.put(indexName, indexInfo);

        }
        return map.keySet().stream().map(map::get).collect(Collectors.toList());
    }

    private void indexColumn(ResultSet rs, IndexInfo indexInfo, List<IndexInfo.IndexColumn> indexColumn) throws SQLException {
        IndexInfo.IndexColumn indexColumn2 = indexInfo.new IndexColumn();
        indexColumn2.setColumnName(rs.getString("column_name"));
        indexColumn2.setOrdinalPosition(rs.getInt("key_seq"));
        indexColumn.add(indexColumn2);
    }


    private List<ColumnInfo> getColumnInfo(String tableViewName, DatabaseMetaData metaData, String schemaPattern, List<GeneralModel> model) throws SQLException {
        CaseInsensitiveMap<String, ColumnInfo> modelMap = ListUtils.GeneralModelListToMap(model);
        List<ColumnInfo> sourceColumn = getSourceColumn(tableViewName, metaData, schemaPattern, modelMap);
        List<ColumnInfo> result = new ArrayList<>();
        GeneralParseTableHandle handle = new GeneralParseTableHandle();
        model.stream().forEach(c -> {
            if (c.getSourceColumn()) {
                for (ColumnInfo columnInfo : sourceColumn) {
                    if (columnInfo.getColumnName().equalsIgnoreCase(c.getColumnName())) {
                        if (StringUtils.isNotBlank(c.getColumnComments())) {
                            columnInfo.setColumnComments(c.getColumnComments());
                        }
                        columnInfo.setColumnName(c.getDestColumnName());
                        result.add(columnInfo);
                        return;
                    }
                }
            } else {
                result.add(handle.getColumnInfo(c));
            }
        });
        return result;
    }

    public List<ColumnInfo> getSourceColumn(String tableViewName, DatabaseMetaData metaData, String schemaPattern) throws SQLException {
        ResultSet rs = metaData.getColumns(null, schemaPattern, tableViewName, "%");
        List<ColumnInfo> sourceCloumn = new ArrayList<>();
        while (rs.next()) {
            ColumnInfo columnInfo = new ColumnInfo();

            columnInfo.setColumnComments(rs.getString("REMARKS"));
            columnInfo.setDataType(rs.getString("TYPE_NAME"));
            columnInfo.setColumnName(rs.getString("COLUMN_NAME"));
            columnInfo.setColumnSize(rs.getInt("COLUMN_SIZE"));
            columnInfo.setDecimalDigits(rs.getInt("DECIMAL_DIGITS"));
            columnInfo.setColumnType(SQLColumnTypeEnum.resolve(rs.getInt("DATA_TYPE")).getColumnEnum());
            //IS_NULLABLE - YES/NO  NULLABLE - 1(允许为空)/2(不确定)/0(不允许为空)
            columnInfo.setNullAbleStatus("yes".equalsIgnoreCase(rs.getString("IS_NULLABLE")) ? 1 : 0);
            sourceCloumn.add(columnInfo);
        }
        rs.close();
        return sourceCloumn;
    }

    public List<ColumnInfo> getSourceColumn(String tableViewName, DatabaseMetaData metaData, String schemaPattern, CaseInsensitiveMap<String, ColumnInfo> modelMap) throws SQLException {

        ResultSet rs = metaData.getColumns(null, schemaPattern, tableViewName, "%");
        List<ColumnInfo> sourceCloumn = new ArrayList<>();
        while (rs.next()) {
            ColumnInfo columnInfo = new ColumnInfo();
            int column_size = rs.getInt("COLUMN_SIZE");
            int decimal_digits = rs.getInt("DECIMAL_DIGITS");
            String column_name = rs.getString("COLUMN_NAME");
            ColumnInfo userColumnInfo = modelMap.get(column_name);
            if (userColumnInfo == null) {
                continue;
            }
            Integer columnSize = userColumnInfo.getColumnSize();
            Integer decimalDigits = userColumnInfo.getDecimalDigits();
            columnSize = columnSize == null ? column_size : columnSize;
            decimalDigits = decimalDigits == null ? decimal_digits : decimalDigits;

            columnInfo.setColumnComments(rs.getString("REMARKS"));
            columnInfo.setDataType(rs.getString("TYPE_NAME"));
            columnInfo.setColumnName(column_name);
            columnInfo.setColumnSize(columnSize);
            columnInfo.setDecimalDigits(decimalDigits);

            columnInfo.setColumnType(SQLColumnTypeEnum.resolve(rs.getInt("DATA_TYPE")).getColumnEnum());
            //IS_NULLABLE - YES/NO  NULLABLE - 1(允许为空)/2(不确定)/0(不允许为空)
            columnInfo.setNullAbleStatus("yes".equalsIgnoreCase(rs.getString("IS_NULLABLE")) ? 1 : 0);
            sourceCloumn.add(columnInfo);
        }
        rs.close();
        return sourceCloumn;
    }
}
