package com.xjrsoft.common.utils;

import cn.hutool.core.util.PageUtil;
import cn.hutool.db.DbUtil;
import cn.hutool.db.Page;
import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.MySqlFieldsType;
import com.xjrsoft.common.enums.OracleFieldsType;
import com.xjrsoft.common.enums.PostgreSqlFieldsType;
import com.xjrsoft.common.enums.SqlServerFieldsType;
import com.xjrsoft.module.generator.entity.TableFieldConfig;
import com.xjrsoft.module.generator.entity.TableStructureConfig;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

/**
 * @title: 数据源工具类
 * @Author tzx
 * @Date: 2022/4/17 14:55
 * @Version 1.0
 */
@Slf4j
public class DatasourceUtil {

    /**
     * 获取主数据源
     *
     * @return
     */
    public static DataSource getDatasourceMaster() {
//        DynamicRoutingDataSource dynamicRoutingDataSource = SpringUtil.getBean(DynamicRoutingDataSource.class);
//        return dynamicRoutingDataSource.getDataSource(GlobalConstant.DEFAULT_DATASOURCE_KEY);
        DruidDataSource druidDataSource = SpringUtil.getBean(DruidDataSource.class);
        return druidDataSource;
    }

    /**
     * 获取数据源
     *
     * @return
     */
    public static DataSource getDataSource(String id) {
//        DynamicRoutingDataSource dynamicRoutingDataSource = SpringUtil.getBean(DynamicRoutingDataSource.class);
//        try {
//            return dynamicRoutingDataSource.getDataSource(id);
//        } catch (Exception e) {
//            throw new RuntimeException("获取数据源失败： " + id);
//        }
        DruidDataSource druidDataSource = SpringUtil.getBean(DruidDataSource.class);
        return druidDataSource;
    }

    public static DbType getDataSourceMasterDbType() {
//        DynamicDataSourceProperties dynamicDataSourceProperties = SpringUtil.getBean(DynamicDataSourceProperties.class);
//        DataSourceProperty datasource = dynamicDataSourceProperties.getDatasource().get(dynamicDataSourceProperties.getPrimary());
        String url = getDruidDataSource().getUrl();
        return JdbcUtils.getDbType(url);
    }

    public static DruidDataSource getDruidDataSource() {
        return SpringUtil.getBean(DruidDataSource.class);
    }


    /**
     * 关闭链接
     *
     * @return
     */
    @SneakyThrows
    public static void close(Connection connection, ResultSet resultSet) {
        if (resultSet != null) {
            resultSet.close();
        }
        if (connection != null) {
            connection.close();
        }
    }

    /**
     * 构建分页查询sql
     *
     * @param sql
     * @param page
     * @return
     */
    public static String wrapperPageSql(String sql, Page page, DbType dbType) {
        switch (dbType) {
            case MYSQL:
                return wrapperPageSqlForMysql(sql, page);
            case ORACLE:
            case ORACLE_12C:
                return wrapperPageSqlForOracle(sql, page);
            case SQL_SERVER:
                return wrapperPageSqlForSqlServer(sql, page);
            case POSTGRE_SQL:
                return wrapperPageSqlForPostgreSql(sql, page);
            case DM:
                return wrapperPageSqlForDm(sql, page);
            case DB2:
                return wrapperPageSqlForDb2(sql, page);
            case KINGBASE_ES:
                return wrapperPageSqlForKingBaseEs(sql, page);
            case GAUSS:
                return wrapperPageSqlForGauss(sql, page);
            default:
                return sql;
        }
    }

    private static String wrapperPageSqlForMysql(String sql, Page page) {
        return "select * from (" + sql + ") t" + " limit " + PageUtil.getStart(page.getPageNumber(), page.getPageSize()) + "," + page.getPageSize();
    }

    private static String wrapperPageSqlForOracle(String sql, Page page) {
        return "select * from ( SELECT row_.*, rownum rownum_ from ( " + sql + " ) row_ where rownum <= " + PageUtil.getStart(page.getPageNumber(), page.getPageSize()) + ") table_alias where table_alias.rownum_ >" + PageUtil.getEnd(page.getPageNumber(), page.getPageSize());
    }

    private static String wrapperPageSqlForSqlServer(String sql, Page page) {
        return "select * FROM ( " + sql + " ) t ORDER BY current_timestamp offset " + PageUtil.getStart(page.getPageNumber(), page.getPageSize()) + " rows fetch next " + PageUtil.getEnd(page.getPageNumber(), page.getPageSize()) + " rows only";
    }

    private static String wrapperPageSqlForPostgreSql(String sql, Page page) {
        return "select * FROM ( " + sql + " ) t limit " + page.getPageNumber() + " offset " + page.getPageSize();
    }

    private static String wrapperPageSqlForDm(String sql, Page page) {
        return "select * from (" + sql + ") t" + " limit " + PageUtil.getStart(page.getPageNumber(), page.getPageSize()) + "," + PageUtil.getEnd(page.getPageNumber(), page.getPageSize());
    }

    private static String wrapperPageSqlForDb2(String sql, Page page) {
        return "select row_num() over() as rownum,* from ( " + sql + " ) where rownum > " + PageUtil.getStart(page.getPageNumber(), page.getPageSize()) + " and rownum < " + PageUtil.getEnd(page.getPageNumber(), page.getPageSize());
    }

    private static String wrapperPageSqlForKingBaseEs(String sql, Page page) {
        return "select * FROM ( " + sql + " ) t limit " + page.getPageNumber() + " offset " + page.getPageSize();
    }

    private static String wrapperPageSqlForGauss(String sql, Page page) {
        return "select * FROM ( " + sql + " ) t limit " + page.getPageNumber() + " offset " + page.getPageSize();
    }

    private static String wrapperPageCount(String sql, Page page) {
        return "select count(*) from (" + sql + ") t";
    }

    /**
     * 构建测试sql
     *
     * @return
     */
    public static String wrapperTestSql(DbType dbType) {
        switch (dbType) {
            case ORACLE:
            case ORACLE_12C:
                return "SELECT 1 FROM DUAL";
            default:
                return "select 1";
        }
    }

    /**
     * 构建测试sql
     *
     * @return
     */
    public static String wrapperDropSql(String tableName) {
        return "drop table " + tableName;
    }


    /**
     * 构建建表sql语句
     *
     * @param tableStructureConfigs
     * @return
     */
    public static List<String> wrapperCreateTableSql(List<TableStructureConfig> tableStructureConfigs, DbType dbType) {
        switch (dbType) {
            case ORACLE:
            case ORACLE_12C:
            case DM:
            case DB2:
                return wrapperCreateTableSqlForOracle(tableStructureConfigs);
            case SQL_SERVER2005:
            case SQL_SERVER:
                return wrapperCreateTableSqlForSqlServer(tableStructureConfigs);
            case POSTGRE_SQL:
            case KINGBASE_ES:
            case GAUSS:
                return wrapperCreateTableSqlForPostgreSql(tableStructureConfigs);
            default:
                return wrapperCreateTableSqlForMysql(tableStructureConfigs);
        }
    }

    private static List<String> wrapperCreateTableSqlForMysql(List<TableStructureConfig> tableStructureConfigs) {
        List<String> sqls = new ArrayList<>();
        //遍历所有表配置
        for (TableStructureConfig tableStructureConfig : tableStructureConfigs) {
            // 创建新表和覆盖旧表才需要创建表
            Integer operator = tableStructureConfig.getOperator();
            if (operator == 1) {
                continue;
            }
            if (operator == 3) {
                sqls.add(wrapperDropSql(tableStructureConfig.getTableName()));
            }
            StringBuilder sb = new StringBuilder();

            sb.append("CREATE TABLE `").append(tableStructureConfig.getTableName()).append("` ( \n");
            sb.append("  `").append(GlobalConstant.DEFAULT_PK).append("` bigint NOT NULL, \n");

            List<TableFieldConfig> tableFieldConfigs = tableStructureConfig.getTableFieldConfigs();

            //遍历当前表配置的 字段配置
            for (TableFieldConfig tableFieldConfig : tableFieldConfigs) {
                MySqlFieldsType fieldType = MySqlFieldsType.getFieldType(tableFieldConfig.getFieldType());
                String type = fieldType.getType();
                switch (fieldType) {
                    //普通文本
                    case VARCHAR:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(").append(tableFieldConfig.getFieldLength()).append(") COMMENT '" + tableFieldConfig.getFieldComment() + "',  \n");
                        break;
                    //长文本
                    case VARCHARMAX:
                        //数字
                    case INT:
                    case FLOAT:
                        //日期
                    case DATE:
                        //日期时间
                    case DATETIME:
                        //长数字
                    case LONG:
                        //长数字
                    case TIME:
                        sb.append("    `").append(tableFieldConfig.getFieldName()).append("` ").append(type).append(" COMMENT '").append(tableFieldConfig.getFieldComment()).append("', \n");
                        break;
                    default:
                        sb.append("    `").append(tableFieldConfig.getFieldName()).append("` ").append(type).append("(").append(GlobalConstant.DEFAULT_TEXT_LENGTH).append(") COMMENT '").append(tableFieldConfig.getFieldComment()).append("', \n");
                        break;
                }

            }


            //如果不是主表 默认需要新增主表关联字段 用于关联主表  默认字段名为 parent + _id
            if (!tableStructureConfig.getIsMain()) {
                sb.append("  `").append(GlobalConstant.DEFAULT_FK).append("` bigint , \n");
            }

            sb.append("  PRIMARY KEY (`").append(GlobalConstant.DEFAULT_PK).append("`) \n");
            sb.append(") ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='").append(tableStructureConfig.getTableComment()).append("';\n");

            sqls.add(sb.toString());
        }
        return sqls;
    }

    private static List<String> wrapperCreateTableSqlForOracle(List<TableStructureConfig> tableStructureConfigs) {
        List<String> sqls = new ArrayList<>();
        //遍历所有表配置
        for (TableStructureConfig tableStructureConfig : tableStructureConfigs) {
            // 创建新表和覆盖旧表才需要创建表
            Integer operator = tableStructureConfig.getOperator();
            if (operator == 1) {
                continue;
            }
            if (operator == 3) {
                sqls.add(wrapperDropSql(tableStructureConfig.getTableName()));
            }
            StringBuilder sb = new StringBuilder();
            sb.append("CREATE TABLE ").append(tableStructureConfig.getTableName()).append(" ( \n");
            sb.append("  ").append(GlobalConstant.DEFAULT_PK).append(" NUMBER(20, 0) NOT NULL, \n");

            List<TableFieldConfig> tableFieldConfigs = tableStructureConfig.getTableFieldConfigs();

            //遍历当前表配置的 字段配置
            for (TableFieldConfig tableFieldConfig : tableFieldConfigs) {
                OracleFieldsType fieldType = OracleFieldsType.getFieldType(tableFieldConfig.getFieldType());
                String type = fieldType.getType();
                switch (fieldType) {
                    //普通文本
                    case VARCHAR:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(").append(tableFieldConfig.getFieldLength()).append("),  \n");
                        break;
                    //数字
                    case INT:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(11, 0),  \n");
                        break;
                    //长数字
                    case LONG:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(20, 0),  \n");
                        break;
                    //小数
                    case FLOAT:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(20, 10),  \n");
                        break;
                    //长文本
                    case VARCHARMAX:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(2000),  \n");
                        break;
                    //日期
                    case DATE:
                    case DATETIME:
                        // 时间
                    case TIME:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append(",  \n");
                        break;
                    default:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(").append(GlobalConstant.DEFAULT_TEXT_LENGTH).append("),  \n");
                        break;
                }

            }
            //如果不是主表 默认需要新增主表关联字段 用于关联主表  默认字段名为 parent + _id
            if (!tableStructureConfig.getIsMain()) {
                sb.append("  ").append(GlobalConstant.DEFAULT_FK).append(" NUMBER(20, 0), \n");
            }


            sb.append("  PRIMARY KEY (").append(GlobalConstant.DEFAULT_PK).append(") \n");
            sb.append(") \n");

            sqls.add(sb.toString());
        }
        return sqls;
    }

    private static List<String> wrapperCreateTableSqlForSqlServer(List<TableStructureConfig> tableStructureConfigs) {
        List<String> sqls = new ArrayList<>();
        //遍历所有表配置
        for (TableStructureConfig tableStructureConfig : tableStructureConfigs) {
            // 创建新表和覆盖旧表才需要创建表
            Integer operator = tableStructureConfig.getOperator();
            if (operator == 1) {
                continue;
            }
            if (operator == 3) {
                sqls.add(wrapperDropSql(tableStructureConfig.getTableName()));
            }

            StringBuilder sb = new StringBuilder();
            sb.append("CREATE TABLE ").append(tableStructureConfig.getTableName()).append(" ( \n");
            sb.append("  ").append(GlobalConstant.DEFAULT_PK).append(" bigint NOT NULL, \n");

            List<TableFieldConfig> tableFieldConfigs = tableStructureConfig.getTableFieldConfigs();

            //遍历当前表配置的 字段配置
            for (TableFieldConfig tableFieldConfig : tableFieldConfigs) {
                SqlServerFieldsType fieldType = SqlServerFieldsType.getFieldType(tableFieldConfig.getFieldType());
                String type = fieldType.getType();
                switch (fieldType) {
                    //普通文本
                    case VARCHAR:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(").append(tableFieldConfig.getFieldLength()).append("),  \n");
                        break;
                    //长文本
                    case VARCHARMAX:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(max),  \n");
                        break;
                    //数字
                    case INT:
                        //小数
                    case FLOAT:
                        //日期
                    case DATE:
                    case DATETIME:
                        //长数字
                    case LONG:
                        // 时间
                    case TIME:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append(",  \n");
                        break;
                    default:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(").append(GlobalConstant.DEFAULT_TEXT_LENGTH).append("),  \n");
                        break;
                }

            }

            //如果不是主表 默认需要新增主表关联字段 用于关联主表  默认字段名为 parent + _id
            if (!tableStructureConfig.getIsMain()) {
                sb.append("  ").append(GlobalConstant.DEFAULT_FK).append(" bigint , \n");
            }

            sb.append("  PRIMARY KEY (").append(GlobalConstant.DEFAULT_PK).append(") \n");
            sb.append("); \n");

            sqls.add(sb.toString());
        }
        return sqls;
    }

    private static List<String> wrapperCreateTableSqlForPostgreSql(List<TableStructureConfig> tableStructureConfigs) {
        List<String> sqls = new ArrayList<>();
        //遍历所有表配置
        for (TableStructureConfig tableStructureConfig : tableStructureConfigs) {
            // 创建新表和覆盖旧表才需要创建表
            Integer operator = tableStructureConfig.getOperator();
            if (operator == 1) {
                continue;
            }
            if (operator == 3) {
                sqls.add(wrapperDropSql(tableStructureConfig.getTableName()));
            }
            StringBuilder sb = new StringBuilder();
            sb.append("CREATE TABLE ").append(tableStructureConfig.getTableName()).append(" ( \n");
            sb.append("  ").append(GlobalConstant.DEFAULT_PK).append(" bigint NOT NULL, \n");

            List<TableFieldConfig> tableFieldConfigs = tableStructureConfig.getTableFieldConfigs();

            //遍历当前表配置的 字段配置
            for (TableFieldConfig tableFieldConfig : tableFieldConfigs) {
                PostgreSqlFieldsType fieldType = PostgreSqlFieldsType.getFieldType(tableFieldConfig.getFieldType());
                String type = fieldType.getType();
                switch (fieldType) {
                    //普通文本
                    case VARCHAR:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(").append(tableFieldConfig.getFieldLength()).append("),  \n");
                        break;
                    //长文本
                    case VARCHARMAX:
                        //数字
                    case INT:
                        //长数字
                    case LONG:
                        //小数
                    case FLOAT:
                        //日期
                    case DATE:
                    case DATETIME:
                    case TIME:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append(",  \n");
                        break;
                    default:
                        sb.append(tableFieldConfig.getFieldName()).append(" ").append(type).append("(").append(GlobalConstant.DEFAULT_TEXT_LENGTH).append("),  \n");
                        break;
                }

            }
            //如果不是主表 默认需要新增主表关联字段 用于关联主表  默认字段名为 parent + _id
            if (!tableStructureConfig.getIsMain()) {
                sb.append("  ").append(GlobalConstant.DEFAULT_FK).append(" ").append(PostgreSqlFieldsType.FK.getType()).append(" , \n");
            }

            sb.append("  PRIMARY KEY (").append(GlobalConstant.DEFAULT_PK).append(") \n");
            sb.append("); \n");
            sqls.add(sb.toString());
        }
        for (TableStructureConfig tableStructureConfig : tableStructureConfigs) {
            List<TableFieldConfig> tableFieldConfigs = tableStructureConfig.getTableFieldConfigs();
            //遍历当前表配置的 字段配置
            for (TableFieldConfig tableFieldConfig : tableFieldConfigs) {
                sqls.add("COMMENT ON COLUMN " + tableStructureConfig.getTableName() + "." + tableFieldConfig.getFieldName() + " IS '" + tableFieldConfig.getFieldComment() + "'");
            }

        }
        return sqls;
    }

    /**
     * 构建 判断表是否存在的sql
     *
     * @param tableName
     * @param dbType
     * @return
     */
    private static String wrapperExistTableSql(String tableName, DbType dbType) {
        switch (dbType) {
            case ORACLE:
            case ORACLE_12C:
                return wrapperExistTableSqlForOracle(tableName);
            case SQL_SERVER:
                return wrapperExistTableSqlForSqlServer(tableName);
            case POSTGRE_SQL:
                return wrapperExistTableSqlForPostgreSql(tableName);
            case DM:
                return wrapperExistTableSqlForDm(tableName);
            case DB2:
                return wrapperExistTableSqlForDb2(tableName);
            case KINGBASE_ES:
                return wrapperExistTableSqlForKingBaseEs(tableName);
            case GAUSS:
                return wrapperExistTableSqlForGauss(tableName);
            default:
                return wrapperExistTableSqlForMysql(tableName);
        }
    }

    private static String wrapperExistTableSqlForMysql(String tableName) {
        return "select count(*) from information_schema.TABLES where TABLE_NAME = '" + tableName + "'";
    }

    private static String wrapperExistTableSqlForOracle(String tableName) {
        return "select count(*) from user_tables where table_name = '" + tableName + "'";
    }

    private static String wrapperExistTableSqlForSqlServer(String tableName) {
        return "select count(*) from sys.objects where object_id = OBJECT_ID('" + tableName + "')";
    }

    private static String wrapperExistTableSqlForPostgreSql(String tableName) {
        return "select count(*) from pg_catalog.pg_tables where tablename = '" + tableName + "'";
    }

    private static String wrapperExistTableSqlForDm(String tableName) {
        return "select count(*) from sysobjects where NAME = '" + tableName + "'";
    }

    private static String wrapperExistTableSqlForDb2(String tableName) {
        return "select count(*) from sysibm.systables where tablename = '" + tableName + "'";
    }

    private static String wrapperExistTableSqlForKingBaseEs(String tableName) {
        return "select count(*) from sys_tables where table_name = '" + tableName + "'";
    }

    private static String wrapperExistTableSqlForGauss(String tableName) {
        return "select count(*) from sys_tables where table_name = '" + tableName + "'";
    }

    public static Boolean testConnection(String url, String userName, String password) {
        Connection conn = null;
        try (SimpleDataSource ds = new SimpleDataSource(url, userName, password)) {
            ds.setLoginTimeout(5);
            conn = ds.getConnection();
        } catch (Exception e) {
            log.error("数据库连接失败！url: " + url, e);
            return false;
        } finally {
            DbUtil.close(conn);
        }
        return true;
    }

    public static String getDbType(DbType dbType, Integer typeCode) {
        switch (dbType) {
            case ORACLE:
            case ORACLE_12C:
            case DM:
            case DB2:
                return OracleFieldsType.getFieldType(typeCode).getType();
            case SQL_SERVER:
                return SqlServerFieldsType.getFieldType(typeCode).getType();
            case POSTGRE_SQL:
            case KINGBASE_ES:
            case GAUSS:
                return PostgreSqlFieldsType.getFieldType(typeCode).getType();
            default:
                return MySqlFieldsType.getFieldType(typeCode).getType();
        }
    }

    public static DbType getDbType(String datasourceId) {
        DataSource dataSource = getDataSource(datasourceId);
        if (dataSource != null) {
            Connection connection = null;
            try {
                connection = dataSource.getConnection();
                return JdbcUtils.getDbType(connection.getMetaData().getURL());
            } catch (Exception e) {
                log.error("获取数据库类型失败！", e);
            } finally {
                DbUtil.close(connection);
            }
        }
        return null;
    }
}
