package cn.iocoder.yudao.module.iot.service.device;

import cn.iocoder.yudao.framework.common.pojo.DeviceEdgeData;
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.JavaToTdengineTypeEnum;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.COLOUMN_CREATION_FAILED;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.TABLE_CREATION_FAILED;

@Service
@Slf4j
public class TDengineService {
    @Resource
    private JdbcTemplate jdbcTemplate;

//    @DS("tdengine")
//    public void testConnection() {
//        String testSQL = "SELECT SERVER_STATUS()";
//        jdbcTemplate.queryForObject(testSQL, Integer.class);
//        log.info("TDengine连接正常");
//
//    }

    @DS("tdengine")
    public void initDatabaseAndTable(Long id) {
        // 1. 创建数据库（使用正确的TDengine语法）
        // 注意：KEEP 必须大于或等于 3 倍的 DURATION[6](@ref)，建议调整
        String createDbSQL = "CREATE DATABASE IF NOT EXISTS besure KEEP 365 DURATION 30";
        jdbcTemplate.execute(createDbSQL);

        // 2. 使用数据库
        jdbcTemplate.execute("USE besure");

        // 3. 创建超级表
        String createSuperTableSQL = "CREATE STABLE IF NOT EXISTS device_data (" +
                "ts TIMESTAMP, " +
                "query_data BLOB" +
                ") TAGS (device_id BIGINT)";
        jdbcTemplate.execute(createSuperTableSQL);

        // 4. 创建子表
        String tableName = "d_" + id;
        String createTableSql = String.format(
                "CREATE TABLE IF NOT EXISTS %s USING device_data TAGS(%d)",
                tableName, id);
        jdbcTemplate.execute(createTableSql);

        log.info("TDengine表创建成功 {}", tableName);

    }


    /**
     * 根据设备ID查询对应子表的最新一条数据
     * 使用TDengine的last_row函数实现高效查询[3,4](@ref)
     *
     * @param id 设备ID，对应子表的tags值
     * @return 最新设备数据，包含时间戳和查询数据
     */
    @DS("tdengine")
    public Map<String, Object> getLatestDeviceData(Long id) {
        String tableName = "d_" + id;

        String sql = "SELECT ts, query_data FROM besure." + tableName +
                " ORDER BY ts DESC LIMIT 1";

        try {
            return jdbcTemplate.queryForObject(sql, new RowMapper<Map<String, Object>>() {
                @Override
                public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Map<String, Object> result = new HashMap<>();
                    // 现在可以直接通过别名获取
                    Timestamp ts = rs.getTimestamp("ts");
                    // 将Timestamp格式化为字符串
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String timestampStr = sdf.format(ts);
                    result.put("timestamp", timestampStr);

                    // 读取 query_data（二进制字段）
                    byte[] blob = rs.getBytes("query_data");
                    result.put("queryData", decodeQueryData(blob));


                    result.put("deviceId", id);
                    result.put("tableName", tableName);
                    return result;
                }
            });
        } catch (Exception e) {
            log.error("查询设备id为 {} 的最新数据时发生异常", id, e);
            return null;
        }
    }



    /**
     * 向指定设备插入数据
     * @param id 设备ID
     * @param queryData 查询数据（JSON字符串或其他文本数据）
     * @return 插入是否成功
     */
    @DS("tdengine")
    public boolean insertDeviceData(Long id, String queryData) {
        return insertDeviceData(id, queryData, new Timestamp(System.currentTimeMillis()));
    }

    /**
     * 向指定设备插入带时间戳的数据
     * @param id 设备ID
     * @param timestamp 时间戳
     * @return 插入是否成功
     */
    @DS("tdengine")
    public boolean insertDeviceData(Long id, String jsonString, Timestamp timestamp) {
        try {
            // 确保使用正确的数据库
            jdbcTemplate.execute("USE besure");

            String tableName = "d_" + id;
            String sql = "INSERT INTO besure." + tableName + " (ts, query_data) VALUES (?, ?)";

            return jdbcTemplate.update(sql, ps -> {
                ps.setTimestamp(1, timestamp);
                // JSON字符串转byte数组
                byte[] blobData = jsonString.getBytes(StandardCharsets.UTF_8);
                ps.setBytes(2, blobData);
            }) > 0;
        } catch (Exception e) {
            log.error("向设备id为 {} 插入数据时发生异常", id, e);
            return false;
        }
    }

    /**
     * 查询指定设备表的全部数据并按时间倒序排序
     * @param id 设备ID
     * @return 设备数据列表，按时间戳倒序排列
     */
    @DS("tdengine")
    public List<Map<String, Object>> getNewestDeviceDataOrderByTimeDesc(Long id) {
        String tableName = "d_" + id;
        String sql = "SELECT ts, query_data FROM besure." + tableName + " ORDER BY ts DESC LIMIT 1";

        try {
            return Collections.singletonList(jdbcTemplate.queryForObject(sql, new RowMapper<Map<String, Object>>() {
                @Override
                public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Map<String, Object> result = new HashMap<>();
                    result.put("timestamp", rs.getTimestamp("ts"));
                    result.put("deviceId", id);

                    // 读取 query_data（二进制字段）
                    byte[] blob = rs.getBytes("query_data");
                    result.put("queryData", decodeQueryData(blob));

                    return result;
                }
            }));
        } catch (EmptyResultDataAccessException e) {
            return Collections.singletonList(createEmptyResult(id));
        } catch (Exception e) {
            log.error("查询设备id为 {} 的最新数据时发生异常", id, e);
            return Collections.singletonList(createEmptyResult(id));
        }
    }


    // 检查是否是十六进制字符串
    private boolean isHexString(String str) {
        if (str == null || str.trim().isEmpty()) {
            return false;
        }
        String s = str.trim();
        // 检查是否只包含十六进制字符（0-9, a-f, A-F）
        return s.matches("^[0-9a-fA-F]+$");
    }

    // 十六进制解码
    private String hexToString(String hex) {
        try {
            // 使用 Apache Commons Codec
            byte[] bytes = Hex.decodeHex(hex);
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (DecoderException e) {
            log.error("十六进制解码失败: {}", e);
            return "";
        }
    }


    private Map<String, Object> createEmptyResult(Long deviceId) {
        Map<String, Object> result = new HashMap<>();
        result.put("deviceId", deviceId);
        result.put("queryData", new ArrayList<>());
        result.put("timestamp", null);
        return result;
    }
    /**
     * 查询指定设备表的全部数据并按时间倒序排序
     * @param id 设备ID
     * @return 设备数据列表，按时间戳倒序排列
     */
    @DS("tdengine")
    public List<Map<String, Object>> getstDeviceDataOrderByTimeDesc(Long id,String StartTime, String EndTime,Integer limit) {
        String tableName = "d_" + id;
        StringBuilder sqlBuilder = new StringBuilder();
        List<Object> params = new ArrayList<>();

        sqlBuilder.append("SELECT ts, query_data FROM besure.").append(tableName).append(" WHERE 1=1");

        if (StartTime != null) {
            // 直接将时间字符串拼接到SQL中
            sqlBuilder.append(" AND ts >= '").append(StartTime).append("'");
        }

        if (EndTime != null) {
            sqlBuilder.append(" AND ts <= '").append(EndTime).append("'");
        }

        sqlBuilder.append(" ORDER BY ts DESC");

        // 添加LIMIT限制
        if (limit != null && limit > 0) {
            sqlBuilder.append(" LIMIT ").append(limit);
        }

        try {
            return jdbcTemplate.query(sqlBuilder.toString(), new RowMapper<Map<String, Object>>() {
                @Override
                public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Map<String, Object> result = new HashMap<>();
                    result.put("timestamp", rs.getTimestamp("ts"));
                    result.put("deviceId", id);

                    // 读取 query_data（二进制字段）
                    byte[] blob = rs.getBytes("query_data");
                    result.put("queryData", decodeQueryData(blob));

                    return result;
                }
            });
        } catch (Exception e) {
//            log.error("查询设备" + id + "的最新数据时发生异常", e);
//            e.printStackTrace();
            return Collections.singletonList(createEmptyResult(id));
        }
    }


    /**
     * 查询指定设备表的全部数据并按时间倒序排序
     * @param id 设备ID
     * @return 设备数据列表，按时间戳倒序排列
     */
    @DS("tdengine")
    public List<Map<String, Object>> getstDeviceDataOrderByTimeDescPage(Long id,String StartTime, String EndTime,Integer page,
                                                                        Integer pageSize) {
        String tableName = "d_" + id;
        StringBuilder sqlBuilder = new StringBuilder();
        List<Object> params = new ArrayList<>();

        sqlBuilder.append("SELECT ts, query_data FROM besure.").append(tableName).append(" WHERE 1=1");

        if (StartTime != null) {
            // 直接将时间字符串拼接到SQL中
            sqlBuilder.append(" AND ts >= '").append(StartTime).append("'");
        }

        if (EndTime != null) {
            sqlBuilder.append(" AND ts <= '").append(EndTime).append("'");
        }

        sqlBuilder.append(" ORDER BY ts DESC");

        // 分页处理
        if (page != null && pageSize != null && page > 0 && pageSize > 0) {
            int offset = (page - 1) * pageSize;
            sqlBuilder.append(" LIMIT ").append(offset).append(",").append(pageSize);
        }

        try {
            return jdbcTemplate.query(sqlBuilder.toString(), new RowMapper<Map<String, Object>>() {
                @Override
                public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Map<String, Object> result = new HashMap<>();
                    result.put("timestamp", rs.getTimestamp("ts"));
                    result.put("deviceId", id);

                    // 读取 query_data（二进制字段）
                    byte[] blob = rs.getBytes("query_data");
                    result.put("queryData", decodeQueryData(blob));

                    return result;
                }
            });
        } catch (Exception e) {
//            log.error("查询设备" + id + "的最新数据时发生异常", e);
//            e.printStackTrace();
            return Collections.singletonList(createEmptyResult(id));
        }
    }


    /**
     * 查询指定设备历史数据的总条数
     *
     * @param deviceId 设备ID
     * @param startTime 开始时间，可为空
     * @param endTime 结束时间，可为空
     * @return 总记录数
     */
    @DS("tdengine")
    public long queryDeviceDataTotal(Long deviceId, String startTime, String endTime) {
        if (deviceId == null) {
            return 0L;
        }

        String tableName = "d_" + deviceId;
        StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM besure." + tableName + " WHERE 1=1");

        if (startTime != null) {
            countSql.append(" AND ts >= '").append(startTime).append("'");
        }

        if (endTime != null) {
            countSql.append(" AND ts <= '").append(endTime).append("'");
        }

        try {
            Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class);
            return total != null ? total : 0L;
        } catch (Exception e) {
            log.error("查询设备 {} 总条数异常", deviceId, e);
            return 0L;
        }
    }


    /**
     * 从 TDengine 中查询设备边缘数据
     *
     * 说明：
     * 1. TDengine 中每个设备对应一张子表，表名规则：d_{deviceId}
     * 2. 根据时间范围查询
     * 3. 根据 latest 参数决定取最新一条还是最早一条
     *
     * @param deviceId  设备ID，同时也是子表后缀
     * @param startTime 起始时间（可以为 null）
     * @param endTime   结束时间（可以为 null）
     * @param latest    true 表示取最新一条，false 表示取最早一条
     * @return 查询结果，包含 timestamp / deviceId / queryData
     */
    @DS("tdengine")
    public Map<String, Object> getDeviceEdgeData(
            Long deviceId,
            String startTime,
            String endTime,
            boolean latest) {

        // TDengine 子表名：d_设备ID
        String tableName = "d_" + deviceId;

        // 构建 SQL
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT ts, query_data ")
                .append("FROM besure.").append(tableName)
                .append(" WHERE 1=1 ");

        // 起始时间条件
        if (startTime != null) {
            sql.append(" AND ts >= '").append(startTime).append("' ");
        }

        // 结束时间条件
        if (endTime != null) {
            sql.append(" AND ts <= '").append(endTime).append("' ");
        }

        // 根据 latest 决定排序方式
        // latest = true  -> 按时间倒序，取最新一条
        // latest = false -> 按时间正序，取最早一条
        sql.append(" ORDER BY ts ")
                .append(latest ? "DESC" : "ASC")
                .append(" LIMIT 1");

        try {
            return jdbcTemplate.query(sql.toString(), rs -> {

                // 没有数据直接返回 null
                if (!rs.next()) {
                    return null;
                }

                Map<String, Object> result = new HashMap<>();
                result.put("timestamp", rs.getTimestamp("ts"));
                result.put("deviceId", deviceId);

                // 读取 query_data（二进制字段）
                byte[] blob = rs.getBytes("query_data");
                result.put("queryData", decodeQueryData(blob));

                return result;
            });
        } catch (Exception e) {
            log.error("TDengine 查询失败，deviceId={} ，td表不存在", deviceId,e);
            return null;
        }
    }


    /**
     * 批量获取设备在时间区间的首末数据
     * @param deviceIds 设备ID列表
     * @param startTime 开始时间, 格式 yyyy-MM-dd HH:mm:ss
     * @param endTime   结束时间, 格式 yyyy-MM-dd HH:mm:ss
     * @return Map<deviceId, DeviceEdgeData>
     */
    @DS("tdengine")
    public Map<Long, DeviceEdgeData> queryDeviceFirstAndLast(Set<Long> deviceIds, String startTime, String endTime) {
        if (deviceIds == null || deviceIds.isEmpty()) {
            return Collections.emptyMap();
        }

        Map<Long, DeviceEdgeData> result = new HashMap<>();

        for (Long deviceId : deviceIds) {
            try {
                // 查询首条数据
                List<Map<String, Object>> firstList = getDeviceFirstData(deviceId, startTime, endTime, 1);
                Map<String, Object> firstData = firstList.isEmpty() ? null : firstList.get(0);

                // 查询末条数据
                List<Map<String, Object>> lastList = getDeviceLastData(deviceId, startTime, endTime, 1);
                Map<String, Object> lastData = lastList.isEmpty() ? null : lastList.get(0);

                DeviceEdgeData edgeData = new DeviceEdgeData();
                edgeData.setDeviceId(deviceId);

                if (firstData != null) {
                    edgeData.setFirstTs(((Timestamp) firstData.get("timestamp")).toLocalDateTime());
                    edgeData.setFirstData(JSON.toJSONString(firstData.get("queryData")));
                }

                if (lastData != null) {
                    edgeData.setLastTs(((Timestamp) lastData.get("timestamp")).toLocalDateTime());
                    edgeData.setLastData(JSON.toJSONString(lastData.get("queryData")));
                }

                result.put(deviceId, edgeData);
            } catch (Exception e) {
                log.error("查询设备首末数据失败, deviceId={}", deviceId, e);
            }
        }

        return result;
    }



    @DS("tdengine")
    public List<Map<String, Object>> getDeviceFirstData(
            Long deviceId,
            String startTime,
            String endTime,
            Integer limit) {

        String tableName = "d_" + deviceId;
        StringBuilder sql = new StringBuilder();

        sql.append("SELECT ts, query_data ")
                .append("FROM besure.").append(tableName)
                .append(" WHERE ts >= '").append(startTime).append("'")
                .append(" AND ts <= '").append(endTime).append("'")
                .append(" ORDER BY ts ASC ");

        if (limit != null && limit > 0) {
            sql.append(" LIMIT ").append(limit);
        }

        return jdbcTemplate.query(sql.toString(), (rs, rowNum) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("timestamp", rs.getTimestamp("ts"));
            map.put("deviceId", deviceId);

            // 读取 query_data（二进制字段）
            byte[] blob = rs.getBytes("query_data");
            map.put("queryData", decodeQueryData(blob));

            return map;
        });
    }

    @DS("tdengine")
    public List<Map<String, Object>> getDeviceLastData(
            Long deviceId,
            String startTime,
            String endTime,
            Integer limit) {

        String tableName = "d_" + deviceId;
        StringBuilder sql = new StringBuilder();

        sql.append("SELECT ts, query_data ")
                .append("FROM besure.").append(tableName)
                .append(" WHERE ts >= '").append(startTime).append("'")
                .append(" AND ts <= '").append(endTime).append("'")
                .append(" ORDER BY ts DESC ");

        if (limit != null && limit > 0) {
            sql.append(" LIMIT ").append(limit);
        }

        return jdbcTemplate.query(sql.toString(), (rs, rowNum) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("timestamp", rs.getTimestamp("ts"));
            map.put("deviceId", deviceId);

            // 读取 query_data（二进制字段）
            byte[] blob = rs.getBytes("query_data");
            map.put("queryData", decodeQueryData(blob));


            return map;
        });
    }



    @DS("tdengine")
    public List<Map<String, Object>> queryLastDataByHour(Long deviceId, LocalDateTime startTime, LocalDateTime endTime) {
        if (deviceId == null) {
            return Collections.emptyList();
        }

        String tableName = "d_" + deviceId;
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT ts, query_data FROM besure.").append(tableName)
                .append(" WHERE ts >= '").append(startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("'")
                .append(" AND ts <= '").append(endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("'")
                .append(" ORDER BY ts ASC"); // 升序，方便取每小时最后一条

        return jdbcTemplate.query(sql.toString(), (rs, rowNum) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("deviceId", deviceId);

            Timestamp ts = rs.getTimestamp("ts");
            map.put("timestamp", ts != null ? ts.toLocalDateTime() : null);

            // 读取 query_data（二进制字段）
            byte[] blob = rs.getBytes("query_data");
            map.put("queryData", decodeQueryData(blob));


            return map;
        });
    }



    @DS("tdengine")
    public List<Map<String, Object>> queryLastDataByHourBatch(
            Set<Long> deviceIds,
            LocalDateTime startTime,
            LocalDateTime endTime) {

        List<Map<String, Object>> result = new ArrayList<>();
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        for (Long deviceId : deviceIds) {
            String sql =
                    "SELECT ts, query_data FROM besure.d_" + deviceId +
                            " WHERE ts >= '" + startTime.format(fmt) + "'" +
                            " AND ts <= '" + endTime.format(fmt) + "'" +
                            " ORDER BY ts ASC";

            jdbcTemplate.query(sql, rs -> {
                Map<String, Object> map = new HashMap<>();
                map.put("deviceId", deviceId);
                map.put("timestamp", rs.getTimestamp("ts").toLocalDateTime());
                map.put("queryData", decodeQueryData(rs.getBytes("query_data")));
                result.add(map);
            });
        }
        return result;
    }


    @DS("tdengine")
    public List<Map<String, Object>> queryLastDataByDaySafe(
            Set<Long> deviceIds,
            LocalDate startDate,
            int days
    ) {
        List<Map<String, Object>> result = new ArrayList<>();

        DateTimeFormatter formatter =
                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        for (int i = 0; i < days; i++) {

            LocalDate day = startDate.plusDays(i);
            LocalDateTime dayStart = day.atStartOfDay();
            LocalDateTime dayEnd = day.plusDays(1).atStartOfDay();

            for (Long deviceId : deviceIds) {

                String tableName = "besure.d_" + deviceId;

                String sql =
                        "SELECT ts, query_data FROM " + tableName +
                                " WHERE ts >= '" + dayStart.format(formatter) + "'" +
                                " AND ts < '" + dayEnd.format(formatter) + "'" +
                                " ORDER BY ts DESC LIMIT 1";

                try {
                    List<Map<String, Object>> rows = jdbcTemplate.query(
                            sql,
                            (rs, rowNum) -> {
                                Map<String, Object> map = new HashMap<>();
                                map.put("deviceId", deviceId);
                                map.put("day", day.toString());

                                Timestamp ts = rs.getTimestamp("ts");
                                map.put("timestamp",
                                        ts != null ? ts.toLocalDateTime() : null);

                                byte[] blob = rs.getBytes("query_data");
                                map.put("queryData", decodeQueryData(blob));

                                return map;
                            }
                    );

                    if (!rows.isEmpty()) {
                        result.add(rows.get(0));
                    }

                } catch (Exception e) {
                    // 表不存在 / 查询失败 → 直接跳过
                    log.warn("Skip TDengine table: {}, reason: {}",
                            tableName, e.getMessage());
                }
            }
        }

        return result;
    }



    private String decodeQueryData(byte[] blob) {
        if (blob == null || blob.length == 0) return "[]";

        String json = new String(blob, StandardCharsets.UTF_8).trim();

        // 去掉外层引号
        if (json.startsWith("\"") && json.endsWith("\"")) {
            json = json.substring(1, json.length() - 1);
        }

        // 如果是十六进制字符串，解码
        if (isHexString(json)) {
            json = hexToString(json);
        }

        return json;
    }


    @DS("tdengine")
    public LocalDateTime selectLatestTs(Long deviceId) {
        String sql = "SELECT ts FROM besure.d_" + deviceId + " ORDER BY ts DESC LIMIT 1";

        final LocalDateTime[] latestTs = {null};

        jdbcTemplate.query(sql, rs -> {
            if (rs.next()) {
                latestTs[0] = rs.getTimestamp("ts").toLocalDateTime();
            }
        });

        return latestTs[0];
    }


    @DS("tdengine")
    public Map<Long, LocalDateTime> selectLatestTsBatch(List<Long> deviceIds) {
        Map<Long, LocalDateTime> result = new HashMap<>();

        for (Long deviceId : deviceIds) {
            String tableName = "besure.d_" + deviceId;
            String sql = "SELECT ts FROM " + tableName + " ORDER BY ts DESC LIMIT 1";

            try {
                LocalDateTime ts = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> rs.getTimestamp("ts").toLocalDateTime());
                result.put(deviceId, ts);
            } catch (Exception e) {
                // 表不存在或者查询失败
                result.put(deviceId, null);
                log.warn("设备: {}, 获取 ts 失败: {}", deviceId, e.getMessage());
            }
        }

        return result;
    }


    //=================================修改数据后新增方法===============================


    @DS("tdengine")
    public void createTdengineTable(Long deviceId, List<DeviceContactModelDO> contactModelList) {
        if (deviceId == null) {
            return;
        }

        // 1. 数据库名
        String dbName = "besure_server";

        // 2. 表名
        String tableName = "d_" + deviceId;

        // 3. 确保数据库存在
        try {
            String createDbSql = "CREATE DATABASE IF NOT EXISTS " + dbName;
            jdbcTemplate.execute(createDbSql);
            log.info("TDengine 数据库确保存在: {}", dbName);
        } catch (Exception e) {
            log.error("TDengine 数据库创建失败: {}", dbName, e);
            throw exception(TABLE_CREATION_FAILED);
        }

        // 4. 构建列SQL，TDengine必须有 ts
        StringBuilder columnsSql = new StringBuilder("ts TIMESTAMP");

        // 5. 遍历 contactModelList 构建列
        if (contactModelList != null && !contactModelList.isEmpty()) {
            for (DeviceContactModelDO contact : contactModelList) {
                String attributeCode = contact.getAttributeCode();
                String dataType = contact.getDataType();

                if (attributeCode == null || dataType == null) {
                    continue;
                }

                // 使用枚举获取 TDengine 类型
                String tdType = JavaToTdengineTypeEnum.getTdTypeByJavaType(dataType);
                if (tdType == null) {
                    tdType = "DOUBLE"; // 默认使用 DOUBLE
                }

                // 拼接列
                columnsSql.append(", ").append(attributeCode).append(" ").append(tdType);
            }
        }

        // 6. 构建完整 SQL
        String createTableSql = "CREATE TABLE IF NOT EXISTS " + dbName + "." + tableName + " ("
                + columnsSql.toString() + ")";

        // 7. 执行创建表
        try {
            jdbcTemplate.execute(createTableSql);
            log.info("TDengine 表创建成功: {}.{}", dbName, tableName);
        } catch (Exception e) {
            log.error("TDengine 表创建失败: {}.{}", dbName, tableName, e);
            throw exception(TABLE_CREATION_FAILED);
        }
    }


    /**
     * 新增td数据列
     * @param deviceId
     * @param deviceContactModel
     */
    @DS("tdengine")
    public void AddTDDatabaseColumn(Long deviceId, DeviceContactModelDO deviceContactModel) {

        if (deviceId == null || deviceContactModel == null) {
            return;
        }

        String attributeCode = deviceContactModel.getAttributeCode();
        String dataType = deviceContactModel.getDataType();

        if (attributeCode == null || dataType == null) {
            return;
        }

        // 表名
        String tableName = "besure_server.d_" + deviceId;

        // Java类型 -> TDengine类型
        String tdType = JavaToTdengineTypeEnum.getTdTypeByJavaType(dataType);
        if (tdType == null) {
            tdType = "DOUBLE";
        }

        // ALTER TABLE SQL
        String alterSql = "ALTER TABLE " + tableName
                + " ADD COLUMN " + attributeCode + " " + tdType;

        try {
            jdbcTemplate.execute(alterSql);
            log.info("TDengine 表新增列成功: table={}, column={}, type={}",
                    tableName, attributeCode, tdType);

        } catch (Exception e) {

            // TDengine 如果列已存在会报错，这里可以忽略
//            if (e.getMessage() != null && e.getMessage().contains("duplicated column name")) {
//                log.warn("列已存在，跳过: table={}, column={}", tableName, attributeCode);
//                return;
//            }

            log.error("TDengine 表新增列失败: table={}, column={}", tableName, attributeCode, e);
            throw exception(COLOUMN_CREATION_FAILED);
        }
    }

    /**
     * 插入数据
     * @param deviceId
     * @param dataList
     * @return
     */
    @DS("tdengine")
    public boolean newInsertDeviceData(Long deviceId, List<DeviceContactModelDO> dataList) {

        if (deviceId == null || dataList == null || dataList.isEmpty()) {
            return false;
        }

        // 表名
        String tableName = "besure_server.d_" + deviceId;

        // 列名构建
        StringBuilder columnBuilder = new StringBuilder("ts");

        // 值占位符
        StringBuilder valueBuilder = new StringBuilder("?");

        // 参数列表
        List<Object> params = new ArrayList<>();

        // TDengine 必须字段
        params.add(new Timestamp(System.currentTimeMillis()));

        for (DeviceContactModelDO model : dataList) {

            if (model == null) {
                continue;
            }

            String column = model.getAttributeCode();
            Object valueObj = model.getAddressValue();
            String javaType = model.getDataType();

            // 1. 基础校验
            if (column == null || column.trim().isEmpty()) {
                continue;
            }

            if (javaType == null || javaType.trim().isEmpty()) {
                javaType = "double";
            }

            // 防止 SQL 注入（只允许字母数字下划线）
            if (!column.matches("^[a-zA-Z0-9_]+$")) {
                log.warn("非法列名，跳过: {}", column);
                continue;
            }

            try {

                // 2. 自动新增列
//                try {
//                    AddTDDatabaseColumn(deviceId, model);
//                } catch (Exception ignored) {
//                    // 忽略已存在异常
//                }

                // 3. 值为空 -> 插入 NULL
                Object convertedValue = null;

                if (valueObj != null) {

                    String value = valueObj.toString();

                    if (!value.isEmpty()) {

                        convertedValue =
                                JavaToTdengineTypeEnum.convertValue(javaType, value);

                    }
                }

                // 4. 拼接 SQL
                columnBuilder.append(", ").append(column);

                valueBuilder.append(", ?");

                params.add(convertedValue);

            } catch (Exception e) {

                log.error("TDengine 数据处理失败: column={}, value={}, type={}",
                        column, valueObj, javaType, e);
            }
        }

        // 没有有效字段不插入
        if (params.size() <= 1) {

            log.warn("TDengine 插入数据为空，跳过 deviceId={}", deviceId);

            return true;
        }

        String sql =
                "INSERT INTO "
                        + tableName
                        + " (" + columnBuilder + ") VALUES (" + valueBuilder + ")";

        try {

            jdbcTemplate.update(sql, params.toArray());

            log.info("TDengine 插入成功: table={}, columnCount={}",
                    tableName, params.size() - 1);


        } catch (Exception e) {

            log.error("TDengine 插入失败: table={}, sql={}", tableName, sql, e);
            return  false;
        }

        return true;
    }


    /**
     * 根据deviceId批量查询最新时间
     * @param deviceIds
     * @return
     */
    @DS("tdengine")
    public Map<Long, LocalDateTime> newSelectLatestTsBatch(List<Long> deviceIds) {
        Map<Long, LocalDateTime> result = new HashMap<>();

        for (Long deviceId : deviceIds) {
            String tableName = "besure_server.d_" + deviceId;
            String sql = "SELECT ts FROM " + tableName + " ORDER BY ts DESC LIMIT 1";

            try {
                LocalDateTime ts = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> rs.getTimestamp("ts").toLocalDateTime());
                result.put(deviceId, ts);
            } catch (Exception e) {
                // 表不存在或者查询失败
                result.put(deviceId, null);
                log.warn("设备: {}, 获取 ts 失败: {}", deviceId, e.getMessage());
            }
        }

        return result;
    }


    /**
     * 查询最新的数据
     * @param deviceId
     * @return
     */
    @DS("tdengine")
    public Map<String, Object> newSelectLatestRow(Long deviceId) {

        if (deviceId == null) {
            return null;
        }

        String tableName = "besure_server.d_" + deviceId;

        String sql = "SELECT * FROM " + tableName + " ORDER BY ts DESC LIMIT 1";

        try {

            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);

            if (list == null || list.isEmpty()) {
                return null;
            }

            Map<String, Object> row = list.get(0);

            // 转换 ts 为 LocalDateTime（推荐）
            Object tsObj = row.get("ts");
            if (tsObj instanceof Timestamp) {
                row.put("ts", ((Timestamp) tsObj).toLocalDateTime());
            }

            log.debug("设备 {} 最新数据: {}", deviceId, row);

            return row;

        } catch (Exception e) {

            log.warn("设备 {} 查询最新数据失败: {}", deviceId, e.getMessage());

            return null;
        }
    }


    /**
     * 查询指定设备在时间范围内的功率数据，并分页
     *
     * @param deviceId 设备ID
     * @param startTime 开始时间（字符串，格式如 "2026-02-09 00:00:00"）
     * @param endTime 结束时间
     * @param page 页码，从1开始
     * @param pageSize 每页条数
     * @return List<Map<String,Object>> 每条记录包含 ts + 属性列
     */
    @DS("tdengine")
    public List<Map<String, Object>> newSelectBatchPage(Long deviceId, String startTime, String endTime,
                                                      Integer page, Integer pageSize) {
        if (deviceId == null || page == null || pageSize == null) {
            return Collections.emptyList();
        }

        String tableName = "besure_server.d_" + deviceId;

        // 计算分页偏移量
        int offset = (page - 1) * pageSize;

        // 构建 SQL
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("SELECT * FROM ").append(tableName)
                .append(" WHERE 1=1 ");

        if (StringUtils.isNotBlank(startTime)) {
            sqlBuilder.append(" AND ts >= '").append(startTime).append("' ");
        }
        if (StringUtils.isNotBlank(endTime)) {
            sqlBuilder.append(" AND ts <= '").append(endTime).append("' ");
        }

        sqlBuilder.append(" ORDER BY ts DESC ")
                .append(" LIMIT ").append(pageSize)
                .append(" OFFSET ").append(offset);

        String sql = sqlBuilder.toString();

        try {
            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);

            // 如果 ts 需要转换为 LocalDateTime
            for (Map<String, Object> row : list) {
                Object tsObj = row.get("ts");
                if (tsObj instanceof Timestamp) {
                    row.put("ts", ((Timestamp) tsObj).toLocalDateTime());
                }
            }

            return list;

        } catch (EmptyResultDataAccessException e) {
            return Collections.emptyList();
        } catch (Exception e) {
            log.error("查询设备 {} 功率数据异常", deviceId, e);
            return Collections.emptyList();
        }
    }

    /**
     * 查询指定设备在时间范围内的数据总条数
     *
     * @param deviceId 设备ID
     * @param startTime 开始时间（可为空）
     * @param endTime 结束时间（可为空）
     * @return 总条数
     */
    @DS("tdengine")
    public long newSelectBatchTotal(Long deviceId, String startTime, String endTime) {
        if (deviceId == null) {
            return 0;
        }

        String tableName = "besure_server.d_" + deviceId;

        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("SELECT COUNT(*) AS total FROM ").append(tableName).append(" WHERE 1=1 ");

        if (StringUtils.isNotBlank(startTime)) {
            sqlBuilder.append(" AND ts >= '").append(startTime).append("' ");
        }
        if (StringUtils.isNotBlank(endTime)) {
            sqlBuilder.append(" AND ts <= '").append(endTime).append("' ");
        }

        String sql = sqlBuilder.toString();

        try {
            // 查询单行结果
            Map<String, Object> result = jdbcTemplate.queryForMap(sql);
            Object totalObj = result.get("total");

            if (totalObj instanceof Number) {
                return ((Number) totalObj).longValue();
            } else {
                return 0;
            }
        } catch (EmptyResultDataAccessException e) {
            return 0;
        } catch (Exception e) {
            log.error("查询设备 {} 数据总条数异常", deviceId, e);
            return 0;
        }
    }


    /**
     * 查询指定设备在时间范围内的最新 N 条数据
     *
     * @param deviceId 设备ID
     * @param startTime 开始时间（可为空）
     * @param endTime 结束时间（可为空）
     * @param limit 查询条数
     * @return 数据列表，每条数据为 Map<String,Object>
     */
    @DS("tdengine")
    public List<Map<String, Object>> newSelectLatestData(Long deviceId, String startTime, String endTime, int limit) {
        if (deviceId == null || limit <= 0) {
            return Collections.emptyList();
        }

        String tableName = "besure_server.d_" + deviceId;

        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("SELECT * FROM ").append(tableName).append(" WHERE 1=1 ");

        if (StringUtils.isNotBlank(startTime)) {
            sqlBuilder.append(" AND ts >= '").append(startTime).append("' ");
        }
        if (StringUtils.isNotBlank(endTime)) {
            sqlBuilder.append(" AND ts <= '").append(endTime).append("' ");
        }

        sqlBuilder.append(" ORDER BY ts DESC "); // 按时间倒序
        sqlBuilder.append(" LIMIT ").append(limit); // 限制条数

        String sql = sqlBuilder.toString();

        try {
            return jdbcTemplate.queryForList(sql);
        } catch (EmptyResultDataAccessException e) {
            return Collections.emptyList();
        } catch (Exception e) {
            log.error("查询设备 {} 最新 {} 条数据异常", deviceId, limit, e);
            return Collections.emptyList();
        }
    }



    @DS("tdengine")
    public Map<Long, Map<String, Object>> queryDevicesLatestRow(
            List<Long> deviceIds,
            String startTime,
            String endTime) {

        if (CollectionUtils.isEmpty(deviceIds)) {
            return Collections.emptyMap();
        }

        Map<Long, Map<String, Object>> result = new HashMap<>();

        for (Long deviceId : deviceIds) {

            String tableName = "besure_server.d_" + deviceId;

            StringBuilder sql = new StringBuilder();

            sql.append("SELECT * FROM ")
                    .append(tableName)
                    .append(" WHERE 1=1 ");

            if (StringUtils.isNotBlank(startTime)) {
                sql.append(" AND ts >= '").append(startTime).append("' ");
            }

            if (StringUtils.isNotBlank(endTime)) {
                sql.append(" AND ts <= '").append(endTime).append("' ");
            }

            sql.append(" ORDER BY ts DESC LIMIT 1");

            try {

                List<Map<String, Object>> list =
                        jdbcTemplate.queryForList(sql.toString());

                if (!list.isEmpty()) {

                    Map<String, Object> row = list.get(0);

                    convertTs(row);

                    result.put(deviceId, row);

                }

            } catch (Exception e) {

                log.error("查询设备最新数据失败 deviceId={}", deviceId, e);

            }
        }

        return result;
    }

    @DS("tdengine")
    public Map<Long, Map<String, Object>> queryDevicesEarliestRow(
            List<Long> deviceIds,
            String startTime,
            String endTime) {

        if (CollectionUtils.isEmpty(deviceIds)) {
            return Collections.emptyMap();
        }

        Map<Long, Map<String, Object>> result = new HashMap<>();

        for (Long deviceId : deviceIds) {

            String tableName = "besure_server.d_" + deviceId;

            StringBuilder sql = new StringBuilder();

            sql.append("SELECT * FROM ")
                    .append(tableName)
                    .append(" WHERE 1=1 ");

            if (StringUtils.isNotBlank(startTime)) {
                sql.append(" AND ts >= '").append(startTime).append("' ");
            }

            if (StringUtils.isNotBlank(endTime)) {
                sql.append(" AND ts <= '").append(endTime).append("' ");
            }

            sql.append(" ORDER BY ts ASC LIMIT 1");

            try {

                List<Map<String, Object>> list =
                        jdbcTemplate.queryForList(sql.toString());

                if (!list.isEmpty()) {

                    Map<String, Object> row = list.get(0);

                    convertTs(row);

                    result.put(deviceId, row);

                }

            } catch (Exception e) {

                log.error("查询设备最早数据失败 deviceId={}", deviceId, e);

            }
        }

        return result;
    }


    /**
     * 查询近七个小时的数据
     * @param deviceIds
     * @param startTime
     * @param endTime
     * @return
     */
    @DS("tdengine")
    public List<Map<String, Object>> newQueryLastDataByHourBatch(
            Set<Long> deviceIds,
            LocalDateTime startTime,
            LocalDateTime endTime) {

        if (deviceIds == null || deviceIds.isEmpty()) {
            return Collections.emptyList();
        }

        List<Map<String, Object>> result = new ArrayList<>();
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String startStr = startTime.format(fmt);
        String endStr = endTime.format(fmt);

        for (Long deviceId : deviceIds) {
            String tableName = "besure_server.d_" + deviceId;

            // 动态查询所有列（*），按时间倒序
            String sql = "SELECT * FROM " + tableName +
                    " WHERE ts >= '" + startStr + "' AND ts <= '" + endStr + "'" +
                    " ORDER BY ts DESC";

            try {
                List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);

                // 用 map 存储每小时最新一条数据
                Map<String, Map<String, Object>> hourMap = new HashMap<>();
                DateTimeFormatter hourFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");

                for (Map<String, Object> row : rows) {
                    LocalDateTime ts = ((Timestamp) row.get("ts")).toLocalDateTime();
                    String hourKey = ts.format(hourFmt);

                    // 每小时只保留最新一条
                    if (!hourMap.containsKey(hourKey)) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("deviceId", deviceId);
                        map.put("timestamp", ts);

                        for (Map.Entry<String, Object> entry : row.entrySet()) {
                            String key = entry.getKey();
                            if (!"ts".equals(key)) {
                                map.put(key, entry.getValue());
                            }
                        }

                        hourMap.put(hourKey, map);
                    }
                }

                result.addAll(hourMap.values());

            } catch (Exception e) {
                log.error("查询设备 {} 数据异常", deviceId, e);
            }
        }

        return result;
    }


    /**
     * 批量查询设备最近 N 天按天汇总数据
     *
     * @param deviceIds 设备ID列表
     * @param startDate 起始日期（包含）
     * @param days 查询天数
     * @return 每条记录 Map 包含 day、deviceId、timestamp、各列数据
     */
    @DS("tdengine")
    public List<Map<String, Object>> queryLastDataByDayBatch(Set<Long> deviceIds, LocalDate startDate, int days) {
        if (deviceIds == null || deviceIds.isEmpty() || days <= 0) {
            return Collections.emptyList();
        }

        List<Map<String, Object>> result = new ArrayList<>();
        LocalDate endDate = startDate.plusDays(days);

        for (Long deviceId : deviceIds) {
            String tableName = "besure_server.d_" + deviceId;

            String sql = String.format(
                    "SELECT *, ts as timestamp, DATE_FORMAT(ts, '%%Y-%%m-%%d') as day " +
                            "FROM %s " +
                            "WHERE ts >= '%s' AND ts < '%s' " +
                            "ORDER BY ts DESC",
                    tableName, startDate.toString(), endDate.toString()
            );

            try {
                result.addAll(jdbcTemplate.queryForList(sql));
            } catch (Exception e) {
                log.error("查询设备 {} 最近 {} 天数据异常", deviceId, days, e);
            }
        }

        return result;
    }




    private void convertTs(Map<String, Object> row) {

        Object tsObj = row.get("ts");

        if (tsObj instanceof Timestamp) {

            row.put("ts",
                    ((Timestamp) tsObj)
                            .toLocalDateTime());
        }
    }


}