package com.ruoyi.interfaces.database;

import com.alibaba.druid.pool.DruidDataSource;
import com.ruoyi.interfaces.domain.SysMetaDatasource;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 运行时数据源管理器
 * 负责动态创建和管理Druid数据源
 */
@Component
public class DynamicDataSourceManager {

    // 使用线程安全的ConcurrentHashMap存储数据源
    private final Map<String, DruidDataSource> dataSourceMap = new ConcurrentHashMap<>();

    public HashMap<String,String> className = new HashMap<String,String>(){
        {
            put("mysql","com.mysql.cj.jdbc.Driver");
        }
    };

    public DataSource getSysDataSource(SysMetaDatasource datasource){

        String url = "jdbc:"+datasource.getDsType()+"://"+datasource.getDsIp()+":"+datasource.getDsPort()+"/"+datasource.getDsDbname();
        return getOrCreateDataSource(url,
                datasource.getDsUser(),
                datasource.getDsPass(),
                className.get(datasource.getDsType())
                );
    }

    /**
     * 创建或获取数据源
     * @param url 数据库连接URL
     * @param username 用户名
     * @param password 密码
     * @param driverClassName 驱动类名
     * @return 数据源实例
     */
    public DataSource getOrCreateDataSource(String url, String username, String password, String driverClassName) {
        String dataSourceKey = generateKey(url, username);

        // 如果已存在，直接返回
        if (dataSourceMap.containsKey(dataSourceKey)) {
            DruidDataSource existingDataSource = dataSourceMap.get(dataSourceKey);
            if (!existingDataSource.isClosed()) {
                return existingDataSource;
            } else {
                // 如果数据源已关闭，移除并重新创建
                dataSourceMap.remove(dataSourceKey);
            }
        }

        // 创建新的数据源
        DruidDataSource dataSource = createDataSource(url, username, password, driverClassName);
        dataSourceMap.put(dataSourceKey, dataSource);

        return dataSource;
    }

    /**
     * 创建Druid数据源
     */
    private DruidDataSource createDataSource(String url, String username, String password, String driverClassName) {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driverClassName);

        // 配置连接池参数
        dataSource.setInitialSize(1);
        dataSource.setMinIdle(1);
        dataSource.setMaxActive(2);
        dataSource.setMaxWait(60000);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        dataSource.setMinEvictableIdleTimeMillis(300000);
        dataSource.setTestWhileIdle(true);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);

        try {
            dataSource.setFilters("stat,wall");
            dataSource.init(); // 显式初始化
        } catch (SQLException e) {
            throw new RuntimeException("初始化数据源失败: " + e.getMessage(), e);
        }

        return dataSource;
    }

    /**
     * 生成数据源唯一键
     */
    private String generateKey(String url, String username) {
        return url + "|" + username;
    }

    /**
     * 获取所有已创建的数据源
     */
    public Map<String, DruidDataSource> getAllDataSources() {
        return new ConcurrentHashMap<>(dataSourceMap);
    }

    /**
     * 关闭指定数据源
     */
    public void closeDataSource(String url, String username) {
        String key = generateKey(url, username);
        DruidDataSource dataSource = dataSourceMap.get(key);
        if (dataSource != null) {
            dataSource.close();
            dataSourceMap.remove(key);
        }
    }

    /**
     * 关闭所有数据源
     */
    @PreDestroy
    public void closeAllDataSources() {
        for (DruidDataSource dataSource : dataSourceMap.values()) {
            if (!dataSource.isClosed()) {
                dataSource.close();
            }
        }
        dataSourceMap.clear();
    }

    /**
     * 检查数据源是否存在且有效
     */
    public boolean containsDataSource(String url, String username) {
        String key = generateKey(url, username);
        DruidDataSource dataSource = dataSourceMap.get(key);
        return dataSource != null && !dataSource.isClosed();
    }
    // 数据库类型枚举
    public enum DatabaseType {
        MYSQL,
        ORACLE,
        SQLSERVER,
        POSTGRESQL,
        DB2,
        SQLITE,
        H2,
        MARIADB,
        DM
    }

    private static final Map<DatabaseType, String> COUNT_SQL_TEMPLATES = new HashMap<>();
    private static final Map<DatabaseType, String> PAGINATION_SQL_TEMPLATES = new HashMap<>();

    static {
        // 初始化计数SQL模板
        COUNT_SQL_TEMPLATES.put(DatabaseType.MYSQL, "SELECT COUNT(*) FROM (%s) AS total_count");
        COUNT_SQL_TEMPLATES.put(DatabaseType.ORACLE, "SELECT COUNT(*) FROM (%s)");
        COUNT_SQL_TEMPLATES.put(DatabaseType.DM, "SELECT COUNT(*) FROM (%s)");
        COUNT_SQL_TEMPLATES.put(DatabaseType.SQLSERVER, "SELECT COUNT(*) FROM (%s) AS total_count");
        COUNT_SQL_TEMPLATES.put(DatabaseType.POSTGRESQL, "SELECT COUNT(*) FROM (%s) AS total_count");
        COUNT_SQL_TEMPLATES.put(DatabaseType.DB2, "SELECT COUNT(*) FROM (%s)");
        COUNT_SQL_TEMPLATES.put(DatabaseType.SQLITE, "SELECT COUNT(*) FROM (%s)");
        COUNT_SQL_TEMPLATES.put(DatabaseType.H2, "SELECT COUNT(*) FROM (%s) AS total_count");
        COUNT_SQL_TEMPLATES.put(DatabaseType.MARIADB, "SELECT COUNT(*) FROM (%s) AS total_count");

        // 初始化分页SQL模板
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.MYSQL, "%s LIMIT %d, %d");
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.ORACLE,
                "SELECT * FROM (SELECT a.*, ROWNUM rnum FROM (%s) a WHERE ROWNUM <= %d) WHERE rnum > %d");
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.SQLSERVER,
                "SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS row_num FROM (%s) AS inner_query) AS outer_query WHERE row_num BETWEEN %d AND %d");
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.POSTGRESQL, "%s LIMIT %d OFFSET %d");
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.DB2,
                "SELECT * FROM (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS row_num FROM (%s) AS inner_query) AS outer_query WHERE row_num BETWEEN %d AND %d");
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.SQLITE, "%s LIMIT %d OFFSET %d");
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.H2, "%s LIMIT %d OFFSET %d");
        PAGINATION_SQL_TEMPLATES.put(DatabaseType.MARIADB, "%s LIMIT %d, %d");
    }

    /**
     * 生成计数SQL
     * @param originalSql 原始SQL
     * @param databaseType 数据库类型
     * @return 计数SQL
     */
    public static String buildCountSql(String originalSql, DatabaseType databaseType) {
        if (!COUNT_SQL_TEMPLATES.containsKey(databaseType)) {
            throw new IllegalArgumentException("不支持的数据库类型: " + databaseType);
        }
        String template = COUNT_SQL_TEMPLATES.get(databaseType);
        return String.format(template, originalSql);
    }

    /**
     * 生成分页SQL
     * @param originalSql 原始SQL
     * @param pageNum 页码（从1开始）
     * @param pageSize 每页大小
     * @param databaseType 数据库类型
     * @return 分页SQL
     */
    public static String buildPaginationSql(String originalSql, int pageNum, int pageSize, DatabaseType databaseType) {
        if (!PAGINATION_SQL_TEMPLATES.containsKey(databaseType)) {
            throw new IllegalArgumentException("不支持的数据库类型: " + databaseType);
        }

        String template = PAGINATION_SQL_TEMPLATES.get(databaseType);

        // 计算起始位置
        int start = (pageNum - 1) * pageSize;

        // 根据不同数据库类型格式化SQL
        switch (databaseType) {
            case MYSQL:
            case MARIADB:
                return String.format(template, originalSql, start, pageSize);

            case ORACLE:
            case DM:
                int end = pageNum * pageSize;
                return String.format(template, originalSql, end, start);

            case SQLSERVER:
            case DB2:
                start = (pageNum - 1) * pageSize + 1;
                int endRow = pageNum * pageSize;
                return String.format(template, originalSql, start, endRow);

            case POSTGRESQL:
            case SQLITE:
            case H2:
                return String.format(template, originalSql, pageSize, start);

            default:
                throw new IllegalArgumentException("不支持的数据库类型: " + databaseType);
        }
    }


    /**
     * 根据数据库连接URL判断数据库类型
     * @param jdbcType JDBC连接URL
     * @return 数据库类型
     */
    public static DatabaseType getDatabaseTypeFromUrl(String jdbcType) {
        if (jdbcType == null) {
            throw new IllegalArgumentException("JDBC URL不能为空");
        }

        if (jdbcType.startsWith("mysql")) {
            return DatabaseType.MYSQL;
        } else if (jdbcType.startsWith("oracle")) {
            return DatabaseType.ORACLE;
        } else if (jdbcType.startsWith("sqlserver")) {
            return DatabaseType.SQLSERVER;
        } else if (jdbcType.startsWith("postgresql")) {
            return DatabaseType.POSTGRESQL;
        } else if (jdbcType.startsWith("db2")) {
            return DatabaseType.DB2;
        } else if (jdbcType.startsWith("sqlite")) {
            return DatabaseType.SQLITE;
        } else if (jdbcType.startsWith("h2")) {
            return DatabaseType.H2;
        } else if (jdbcType.startsWith("mariadb")) {
            return DatabaseType.MARIADB;
        } else if (jdbcType.startsWith("dm")) {
            return DatabaseType.DM;
        } else {
            throw new IllegalArgumentException("无法识别的数据库连接URL: " + jdbcType);
        }
    }
    /**
     * 生成分页SQL（通过JDBC URL自动识别数据库类型）
     * @param originalSql 原始SQL
     * @param pageNum 页码（从1开始）
     * @param pageSize 每页大小
     * @param jdbcType JDBC连接URL
     * @return 分页SQL
     */
    public static String buildPaginationSql(String originalSql, int pageNum, int pageSize, String jdbcType) {
        DatabaseType databaseType = getDatabaseTypeFromUrl(jdbcType);
        return buildPaginationSql(originalSql, pageNum, pageSize, databaseType);
    }

    /**
     * 生成计数SQL（通过JDBC URL自动识别数据库类型）
     * @param originalSql 原始SQL
     * @param jdbcType JDBC连接URL
     * @return 计数SQL
     */
    public static String buildCountSql(String originalSql, String jdbcType) {
        DatabaseType databaseType = getDatabaseTypeFromUrl(jdbcType);
        return buildCountSql(originalSql, databaseType);
    }



}