package com.ruoyi.system.service;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.sql.*;
import java.util.*;
import java.util.concurrent.*;

@Service
public class TDengineService {
    private static final Logger log = LoggerFactory.getLogger(TDengineService.class);

    // 线程池
    private final ExecutorService batchExecutor = new ThreadPoolExecutor(
            4, 8, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            r -> {
                Thread t = new Thread(r, "tdengine-batch-" + UUID.randomUUID().toString().substring(0, 4));
                t.setDaemon(true);
                return t;
            },
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    private final HikariDataSource dataSource;

    // === 新增：缓存超级表结构 (key = dbName.stableName) ===
    private final Map<String, Set<String>> stableColumnCache = new ConcurrentHashMap<>();

    public TDengineService() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:TAOS://localhost:6030/?user=root&password=taosdata");
        config.setDriverClassName("com.taosdata.jdbc.TSDBDriver");
        config.setMaximumPoolSize(20);
        config.setMinimumIdle(5);
        config.setConnectionTimeout(10000);
        config.setIdleTimeout(30000);
        config.setMaxLifetime(600000);
        config.setLeakDetectionThreshold(0); // 手动管理连接
        config.setConnectionTestQuery("SELECT NOW()");
        config.setValidationTimeout(3000);
        this.dataSource = new HikariDataSource(config);
        log.info("✅ TDengine 连接池初始化完成 (LeakDetection 已禁用，采用手动管理)");
    }

    /**
     * 手动获取连接
     */
    public Connection getConnection() throws SQLException {
        if (dataSource == null) throw new SQLException("数据源未初始化");
        Connection conn = dataSource.getConnection();
        if (conn == null) throw new SQLException("获取连接为 null");
        conn.setAutoCommit(true);
        return conn;
    }

    /**
     * 手动关闭连接
     */
    private void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                if (!conn.isClosed()) {
                    conn.close();
                }
            } catch (SQLException e) {
                log.warn("关闭 TDengine 连接时发生警告: {}", e.getMessage());
            }
        }
    }

    private String escapeValue(String value) {
        if (value == null) return "";
        return value.replace("'", "''");
    }

    private String wrapName(String name) {
        if (name == null || name.isEmpty()) return "`unknown`";
        return "`" + name.replace("`", "") + "`";
    }

    // === 缓存工具方法 ===
    private String getStableKey(String dbName, String stableName) {
        return dbName + "." + stableName;
    }

    // ==========================================
    // 查询列（带缓存 + 超时）
    // ==========================================
    private Set<String> getStableColumns(String dbName, String stableName) {
        String key = getStableKey(dbName, stableName);
        Set<String> cached = stableColumnCache.get(key);
        if (cached != null) {
            return cached;
        }

        // 缓存未命中，查 DB
        Set<String> columns = loadStableColumnsFromDB(dbName, stableName);
        if (!columns.isEmpty()) {
            stableColumnCache.put(key, columns);
        }
        return columns;
    }

    private Set<String> loadStableColumnsFromDB(String dbName, String stableName) {
        Set<String> columns = new HashSet<>();
        String sql = String.format("DESCRIBE %s.%s", wrapName(dbName), wrapName(stableName));
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = getConnection();
            stmt = conn.createStatement();
            stmt.setQueryTimeout(5); // ⭐ 防止无限等待
            rs = stmt.executeQuery(sql);
            while (rs.next()) {
                columns.add(rs.getString(1));
            }
        } catch (SQLException e) {
            if (!e.getMessage().contains("Table does not exist") && !e.getMessage().contains("invalid name")) {
                log.error("查询超级表列失败: {}.{} | {}", dbName, stableName, e.getMessage());
            }
        } finally {
            if (rs != null) try { rs.close(); } catch (SQLException ignored) {}
            if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
            closeConnection(conn);
        }
        return columns;
    }

    // ==========================================
    // 新增列（成功后更新缓存）
    // ==========================================
    private boolean addColumnToStable(String dbName, String stableName, String columnName) {
        String sql = String.format("ALTER TABLE %s.%s ADD COLUMN %s VARCHAR(50)",
                wrapName(dbName), wrapName(stableName), wrapName(columnName));

        Connection conn = null;
        Statement stmt = null;
        try {
            conn = getConnection();
            stmt = conn.createStatement();
            stmt.setQueryTimeout(10);
            stmt.executeUpdate(sql);
            log.info("✅ 新增列成功: {}.{}.{}", dbName, stableName, columnName);

            // ✅ 更新缓存
            String key = getStableKey(dbName, stableName);
            stableColumnCache.computeIfPresent(key, (k, existingCols) -> {
                Set<String> newCols = new HashSet<>(existingCols);
                newCols.add(columnName);
                return newCols;
            });

            return true;
        } catch (SQLException e) {
            if (e.getMessage().contains("already exists")) {
                // 列已存在，确保缓存包含它（防御性）
                String key = getStableKey(dbName, stableName);
                stableColumnCache.computeIfPresent(key, (k, cols) -> {
                    if (!cols.contains(columnName)) {
                        Set<String> updated = new HashSet<>(cols);
                        updated.add(columnName);
                        return updated;
                    }
                    return cols;
                });
                return true;
            }
            if (e.getMessage().contains("Row length exceeds") || e.getMessage().contains("too many columns")) {
                log.warn("⚠️ 跳过新增列 (超限): {}", columnName);
                return false;
            }
            log.error("❌ 新增列失败: {}", e.getMessage());
            return false;
        } finally {
            if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
            closeConnection(conn);
        }
    }

//    private Map<String, String> filterEmptyFields(Map<String, Object> fieldValues) {
//        Map<String, String> filtered = new HashMap<>();
//        for (Map.Entry<String, Object> entry : fieldValues.entrySet()) {
//            if (entry.getValue() != null) {
//                String val = entry.getValue().toString().trim();
//                if (!val.isEmpty()) {
//                    filtered.put(entry.getKey(), escapeValue(val));
//                }
//            }
//        }
//        return filtered;
//    }

    // ==========================================
    // 初始化表结构
    // ==========================================
    private boolean initTableStructure(String dbName, String supertablename, String table, Set<String> fieldNames) {
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = getConnection();
            stmt = conn.createStatement();
            stmt.setQueryTimeout(10);

            stmt.executeUpdate("CREATE DATABASE IF NOT EXISTS " + wrapName(dbName));

            String stableSql = String.format(
                    "CREATE STABLE IF NOT EXISTS %s.%s (ts TIMESTAMP, surfacename VARCHAR(64)) TAGS (location BINARY(64))",
                    wrapName(dbName),
                    wrapName(supertablename)
            );
            stmt.executeUpdate(stableSql);

            Set<String> existing = getStableColumns(dbName, supertablename);
            for (String field : fieldNames) {
                if ("ts".equalsIgnoreCase(field) || "surfacename".equalsIgnoreCase(field)) continue;
                if (!existing.contains(field)) {
                    addColumnToStable(dbName, supertablename, field);
                }
            }

            String tableSql = String.format(
                    "CREATE TABLE IF NOT EXISTS %s.%s USING %s.%s TAGS ('%s')",
                    wrapName(dbName),
                    wrapName(table),
                    wrapName(dbName),
                    wrapName(supertablename),
                    escapeValue(supertablename)
            );
            stmt.executeUpdate(tableSql);

            return true;

        } catch (SQLException e) {
            log.error("❌ 表结构初始化失败: {}.{} | {}", dbName, table, e.getMessage());
            return false;
        } finally {
            if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
            closeConnection(conn);
        }
    }

    // ==========================================
    // 批量插入
    // ==========================================
    public boolean insertBatch(String dbName, String table, List<Map<String, Object>> dataList) throws Exception {
        if (dataList == null || dataList.isEmpty()) return true;

        String supertablename = table.contains("_") ? table.substring(0, table.lastIndexOf('_')) : table;

        Set<String> allFields = new HashSet<>();
        for (Map<String, Object> data : dataList) {
            if (data != null) {
                for (String key : data.keySet()) {
                    if (!"topic".equalsIgnoreCase(key)) allFields.add(key);
                }
            }
        }
        allFields.remove("ts");
        allFields.remove("surfacename");

        initTableStructure(dbName, supertablename, table, allFields);

        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("INSERT INTO ").append(wrapName(dbName)).append(".").append(wrapName(table))
                .append(" (ts, surfacename");

        List<String> colList = new ArrayList<>(allFields);
        for (String col : colList) {
            sqlBuilder.append(", ").append(wrapName(col));
        }
        sqlBuilder.append(") VALUES ");

        boolean hasData = false;
        for (Map<String, Object> data : dataList) {
            if (data == null) continue;
            sqlBuilder.append("(NOW(), '").append(escapeValue(supertablename)).append("'");
            for (String col : colList) {
                Object v = data.get(col);
                String val = (v == null) ? "" : escapeValue(v.toString());
                sqlBuilder.append(", '").append(val).append("'");
            }
            sqlBuilder.append("),");
            hasData = true;
        }

        if (!hasData) return true;

        sqlBuilder.setLength(sqlBuilder.length() - 1);
        String finalSql = sqlBuilder.toString();

        Connection conn = null;
        Statement stmt = null;
        try {
            conn = getConnection();
            stmt = conn.createStatement();
            stmt.setQueryTimeout(30);
            stmt.executeUpdate(finalSql);
            log.info("✅ 批量写入成功: {} | 条数: {}", table, dataList.size());
            return true;
        } catch (SQLException e) {
            log.error("❌ 批量写入 SQL 失败: {} | 错误: {}", table, e.getMessage());
            throw e;
        } finally {
            if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
            closeConnection(conn);
        }
    }

    @Deprecated
    public boolean addToBatch(String dbName, String table, Map<String, Object> dataMap) {
        List<Map<String, Object>> list = new ArrayList<>();
        list.add(dataMap);
        try {
            return insertBatch(dbName, table, list);
        } catch (Exception e) {
            log.error("单条转批量写入失败", e);
            return false;
        }
    }

    // ==========================================
    // 可选：清除缓存（用于运维或测试）
    // ==========================================
    public void clearStableColumnCache() {
        stableColumnCache.clear();
        log.info("🧹 清除了 TDengine 超级表结构缓存");
    }

    public void close() {
        log.info("关闭 TDengine 服务...");
        batchExecutor.shutdown();
        if (dataSource != null) {
            dataSource.close();
        }
    }
}