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 TDengineConnecting{
        private static final Logger log = LoggerFactory.getLogger(com.ruoyi.system.service.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 TDengineConnecting() {
            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 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;

            // 1. 提取超级表名 (去掉 _20264 后缀)
            String supertablename = table.contains("_") ? table.substring(0, table.lastIndexOf('_')) : table;

            // 2.解析 k-v 数据，转换为标准行数据
            Map<String, Object> rowData = new HashMap<>();
            String timeValue = null; // 临时存储时间

            for (Map<String, Object> item : dataList) {
                Object kObj = item.get("k");
                Object vObj = item.get("v");
                Object tObj = item.get("t");

                if (kObj != null && vObj != null) {
                    String k = kObj.toString();
                    // 过滤掉不需要的字段
                    if (!"ts".equals(k) && !"surfacename".equals(k)) {
                        rowData.put(k, vObj);
                    }
                }
                // 提取时间 (假设所有数据的时间是一样的，取第一个即可)
                if (timeValue == null && tObj != null) {
                    timeValue = tObj.toString();
                }
            }
            // 如果没有数据，直接返回
            if (rowData.isEmpty()) return true;
            // 3. 收集所有列名 (用于建表和拼接 SQL)
            Set<String> allFields = rowData.keySet();
            // 4. 自动建表 (如果表不存在)
            initTableStructure(dbName, supertablename, table, allFields);
            // 5. 构建 SQL
            StringBuilder sqlBuilder = new StringBuilder();
            sqlBuilder.append("INSERT INTO ").append(wrapName(dbName)).append(".").append(wrapName(table))
                    .append(" (ts, surfacename");
            // 将 Set 转为 List 保证顺序一致
            List<String> colList = new ArrayList<>(allFields);
            for (String col : colList) {
                sqlBuilder.append(", ").append(wrapName(col));
            }
            sqlBuilder.append(") VALUES ");

            // 6. 拼接数值
            // 注意：这里我们只插入一行数据 (rowData)，而不是遍历原来的 dataList
            sqlBuilder.append("(");

            // 处理时间：如果有 t 字段就用 t，否则用 NOW()
            if (timeValue != null) {
                // 简单处理：直接拼接字符串。
                // 注意：TDengine 接受 'yyyy-MM-dd HH:mm:ss.SSS' 格式。
                // 如果你的 t 是 "2026-03-27 14:14:33,829.323000"，可能需要替换逗号为点
                String cleanTime = timeValue.replace(',', '.');
                sqlBuilder.append("'").append(cleanTime).append("'");
            } else {
                sqlBuilder.append("NOW()");
            }

            sqlBuilder.append(", '").append(escapeValue(supertablename)).append("'");

            // 处理其他列的值
            for (String col : colList) {
                Object v = rowData.get(col);
                String val = (v == null) ? "" : escapeValue(v.toString());
                sqlBuilder.append(", '").append(val).append("'");
            }
            sqlBuilder.append(")");

            String finalSql = sqlBuilder.toString();

            // 7. 执行 SQL
            Connection conn = null;
            Statement stmt = null;
            try {
                conn = getConnection();
                stmt = conn.createStatement();
                stmt.setQueryTimeout(30);
                stmt.executeUpdate(finalSql);
                log.info("✅ 批量写入成功: {} | 字段数: {}", table, colList.size());
                return true;
            } catch (SQLException e) {
                log.error("❌ 批量写入 SQL 失败: {} | 错误: {}", table, e.getMessage());
                log.error("SQL: {}", finalSql); // 打印 SQL 方便调试
                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();
            }
        }
    }