package org.springblade.datasource.transform.sqlserver;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.CaseInsensitiveMap;
import com.alibaba.druid.util.JdbcUtils;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springblade.datasource.dialect.SqlServerGmetryDialect;
import org.springblade.datasource.model.TableColumnDto;
import org.springblade.datasource.model.TableViewDto;
import org.springblade.datasource.support.DynamicDataSourceSupport;
import org.springblade.datasource.transform.ColumnInfo;
import org.springblade.datasource.transform.IndexInfo;
import org.springblade.datasource.transform.TableInfo;
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/8/24 14:55
 */
@Slf4j
public class SqlserverParseTableHandle {

    public List<ColumnInfo> getSourceColumn(ReportDatasource datasource, String tableViewName, SqlServerGmetryDialect sqlServerGmetryDialect) throws SQLException {


        List<TableColumnDto> tableColumnDtos = sqlServerGmetryDialect.listTableColumnsByTableViewName(datasource, tableViewName);

        List<ColumnInfo> sourceColumn = new ArrayList<>();
        for (TableColumnDto tableColumnDto : tableColumnDtos) {

            ColumnInfo columnInfo = new ColumnInfo();

            columnInfo.setColumnComments(tableColumnDto.getColumnComments());
            columnInfo.setDataType(tableColumnDto.getDataType());
            columnInfo.setColumnName(tableColumnDto.getColumnName());
            columnInfo.setColumnSize(tableColumnDto.getColumnSize());
            columnInfo.setDecimalDigits(tableColumnDto.getDecimalDigits());
            SQLColumnTypeEnum resolve = SQLColumnTypeEnum.getByName(tableColumnDto.getDetailColumnType().name());
            if (resolve == null) {
                resolve = SQLColumnTypeEnum.BIG_DECIMAL;
            }
            columnInfo.setColumnType(resolve.getColumnEnum());
            //IS_NULLABLE - YES/NO  NULLABLE - 1(允许为空)/2(不确定)/0(不允许为空)
            columnInfo.setNullAbleStatus(1);
            sourceColumn.add(columnInfo);
        }
        return sourceColumn;
    }

    public List<ColumnInfo> getSourceColumn(ReportDatasource datasource, String tableViewName, SqlServerGmetryDialect sqlServerGmetryDialect, CaseInsensitiveMap<String, ColumnInfo> modelMap) throws SQLException {


        List<TableColumnDto> tableColumnDtos = sqlServerGmetryDialect.listTableColumnsByTableViewName(datasource, tableViewName);

        List<ColumnInfo> sourceColumn = new ArrayList<>();
        for (TableColumnDto tableColumnDto : tableColumnDtos) {
            ColumnInfo userColumnInfo = modelMap.get(tableColumnDto.getColumnName());
            if (userColumnInfo == null) {
                continue;
            }
            Integer columnSize = userColumnInfo.getColumnSize();
            Integer decimalDigits = userColumnInfo.getDecimalDigits();

            ColumnInfo columnInfo = new ColumnInfo();
            columnInfo.setColumnComments(tableColumnDto.getColumnComments());
            columnInfo.setDataType(tableColumnDto.getDataType());
            columnInfo.setColumnName(tableColumnDto.getColumnName());
            columnInfo.setColumnSize(columnSize);
            columnInfo.setDecimalDigits(decimalDigits);
            SQLColumnTypeEnum resolve = SQLColumnTypeEnum.getByName(tableColumnDto.getDetailColumnType().name());
            if (resolve == null) {
                resolve = SQLColumnTypeEnum.BIG_DECIMAL;
            }
            columnInfo.setColumnType(resolve.getColumnEnum());
            //IS_NULLABLE - YES/NO  NULLABLE - 1(允许为空)/2(不确定)/0(不允许为空)
            columnInfo.setNullAbleStatus(1);
            sourceColumn.add(columnInfo);
        }
        return sourceColumn;
    }

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

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

            return tableInfo;
        } finally {
            JdbcUtils.close(connection);
        }
    }

    @SneakyThrows
    public TableInfo getTables(ReportDatasource datasource, String tableViewName, TableInfo tableInfo, SqlServerGmetryDialect sqlServerGmetryDialect) {
        List<TableViewDto> tableViewDtos = sqlServerGmetryDialect.listTableViewsByDatasource(datasource, tableViewName);
        if (CollectionUtil.isNotEmpty(tableViewDtos)) {
            TableViewDto tableViewDto = tableViewDtos.get(0);
            tableInfo.setTableComment(tableViewDto.getTableComments());
        }

        return tableInfo;
    }

    private List<IndexInfo> getPrimaryKeys(List<GeneralModel> model, String tableViewName, DatabaseMetaData metaData, String schemaPattern) throws SQLException {
        ResultSet rs = null;
        HashMap<String, IndexInfo> map = new HashMap<>();
        boolean flag = false;
        for (GeneralModel generalModel : model) {
            if (generalModel.getPrimaryKey()) {
                IndexInfo indexInfo = new IndexInfo();
                indexInfo.setPrimaryKey(true);
                indexInfo.setIndexName(generalModel.getColumnName());
                IndexInfo.IndexColumn indexColumn2 = indexInfo.new IndexColumn();

                indexColumn2.setColumnName(generalModel.getColumnName());
                indexColumn2.setOrdinalPosition(0);
                ArrayList<IndexInfo.IndexColumn> indexColumns = Lists.newArrayList();
                indexColumns.add(indexColumn2);
                indexInfo.setIndexColumn(indexColumns);

                map.put(generalModel.getColumnName(), indexInfo);
                flag = true;
            }
        }
        if (flag) {
            return map.keySet().stream().map(map::get).collect(Collectors.toList());
        }
        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(ReportDatasource datasource, String tableViewName, List<GeneralModel> model, SqlServerGmetryDialect sqlServerGmetryDialect) throws SQLException {
        CaseInsensitiveMap<String, ColumnInfo> modelMap = ListUtils.GeneralModelListToMap(model);

        List<ColumnInfo> sourceColumn = getSourceColumn(datasource, tableViewName, sqlServerGmetryDialect, 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;
    }


}
