package com.usky.ems.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.usky.common.core.bean.ApiResult;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.demo.RemoteTsdbProxyService;
import com.usky.demo.domain.HistorysInnerRequestVO;
import com.usky.demo.domain.HistorysInnerResultVO;
import com.usky.demo.domain.MetricVO;
import com.usky.ems.domain.BaseBuild;
import com.usky.ems.domain.BaseSpaceBuild;
import com.usky.ems.domain.BaseSpaceGateway;
import com.usky.ems.domain.DmpDevice;
import com.usky.ems.mapper.BaseBuildMapper;
import com.usky.ems.mapper.BaseSpaceBuildMapper;
import com.usky.ems.mapper.BaseSpaceGatewayMapper;
import com.usky.ems.mapper.DmpDeviceMapper;
import com.usky.ems.mapper.EmsDeviceItemCodeMapper;
import com.usky.ems.mapper.EmsDeviceMapper;
import com.usky.ems.mapper.EmsEnergyItemCodeMapper;
import com.usky.ems.service.EmsAnalysisService;
import com.usky.ems.service.TdengineService;
import com.usky.ems.service.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 能耗分析服务实现
 */
@Slf4j
@Service
@DS("mysql")
public class EmsAnalysisServiceImpl implements EmsAnalysisService {

    private static final String[] ENERGY_TYPE_NAMES = {"", "电", "水", "气"};

    // 标准煤转换系数
    private static final Map<Long, BigDecimal> STANDARD_COAL_FACTORS = new HashMap<Long, BigDecimal>();

    static {
        STANDARD_COAL_FACTORS.put(Long.valueOf(1), new BigDecimal("0.1229")); // 电
        STANDARD_COAL_FACTORS.put(Long.valueOf(2), new BigDecimal("0.0857")); // 水  
        STANDARD_COAL_FACTORS.put(Long.valueOf(3), new BigDecimal("0.2143")); // 气
    }

    // 碳排放转换系数
    private static final Map<Long, BigDecimal> CARBON_EMISSION_FACTORS = new HashMap<Long, BigDecimal>();

    static {
        CARBON_EMISSION_FACTORS.put(Long.valueOf(1), new BigDecimal("0.7850")); // 电
        CARBON_EMISSION_FACTORS.put(Long.valueOf(2), new BigDecimal("0.2600")); // 水
        CARBON_EMISSION_FACTORS.put(Long.valueOf(3), new BigDecimal("0.4920")); // 气
    }

    // 能源单位
    private static final Map<Long, String> ENERGY_UNITS = new HashMap<Long, String>();

    static {
        ENERGY_UNITS.put(Long.valueOf(1), "kWh"); // 电
        ENERGY_UNITS.put(Long.valueOf(2), "m³");   // 水
        ENERGY_UNITS.put(Long.valueOf(3), "m³");   // 气
    }

    private static final String ENERGY_NO_DATA = "-";
    private static final String ENERGY_METRIC = "totalactiveenergyf";
    private static final int AVERAGE_TYPE_TOTAL = 0;
    private static final int AVERAGE_TYPE_PER_AREA = 1;
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final int USAGE_SCALE = 2;

    @Autowired
    private TdengineService tdengineService;


    @Autowired
    private EmsDeviceMapper emsDeviceMapper;

    @Autowired
    private EmsEnergyItemCodeMapper emsEnergyItemCodeMapper;

    @Autowired
    private EmsDeviceItemCodeMapper emsDeviceItemCodeMapper;

    @Autowired
    private BaseSpaceGatewayMapper baseSpaceGatewayMapper;

    @Autowired
    private BaseSpaceBuildMapper baseSpaceBuildMapper;

    @Autowired
    private BaseBuildMapper baseBuildMapper;

    @Autowired
    private DmpDeviceMapper dmpDeviceMapper;

    @Autowired
    private RemoteTsdbProxyService remoteTsdbProxyService;

    @Autowired
    private DataSource mysqlDataSource;

    @Override
    public EmsTrendResponse getTrend(Long projectId, String timeDimension, String timeValue, Long energyTypeId) {
        EmsTrendResponse resp = new EmsTrendResponse();
        resp.setTimeDimension(timeDimension);
        resp.setTimeValue(timeValue);

        try {
            // 获取项目下的所有设备
            List<String> deviceIds = getDeviceIdsByProject(projectId, energyTypeId);
            if (deviceIds.isEmpty()) {
                return resp;
            }

            // 根据时间维度生成时间点
            List<String> timePoints = generateTimePoints(timeDimension, timeValue);

            // 查询每个时间点的能耗数据
            List<EmsTrendItemVO> trendItems = new ArrayList<>();
            for (String timePoint : timePoints) {
                EmsTrendItemVO item;
                if (energyTypeId == null) {
                    // 当不指定能源类型时，查询项目中所有能源类型设备的总能耗
                    item = calculateTrendItemForAllEnergyTypes(projectId, timeDimension, timePoint);
                } else {
                    item = calculateTrendItem(deviceIds, timeDimension, timePoint, energyTypeId);
                }
                trendItems.add(item);
            }

            resp.setTrend(trendItems);
        } catch (Exception e) {
            // 日志记录错误，返回空数据
            e.printStackTrace();
        }

        return resp;
    }

    /**
     * 生成时间点列表
     */
    private List<String> generateTimePoints(String timeDimension, String timeValue) {
        List<String> timePoints = new ArrayList<>();

        try {
            switch (timeDimension.toUpperCase()) {
                case "D": // 日维度，生成该日每小时
                    timePoints = generateHourlyPoints(timeValue);
                    break;
                case "M": // 月维度，生成该月每天
                    timePoints = generateDailyPoints(timeValue);
                    break;
                case "Y": // 年维度，生成该年每月
                    timePoints = generateMonthlyPoints(timeValue);
                    break;
                default:
                    timePoints.add(timeValue);
            }
        } catch (Exception e) {
            // 如果解析出错，返回原始时间值
            timePoints.add(timeValue);
        }

        return timePoints;
    }

    /**
     * 生成日维度时间点
     */
    private List<String> generateDailyPoints(String yearMonth) {
        List<String> days = new ArrayList<>();
        try {
            String[] parts = yearMonth.split("-");
            int year = Integer.parseInt(parts[0]);
            int month = Integer.parseInt(parts[1]);

            java.time.YearMonth ym = java.time.YearMonth.of(year, month);
            int daysInMonth = ym.lengthOfMonth();

            for (int day = 1; day <= daysInMonth; day++) {
                days.add(year + "-" + (month < 10 ? "0" : "") + month + "-" + (day < 10 ? "0" : "") + day);
            }
        } catch (Exception e) {
            days.add(yearMonth);
        }
        return days;
    }

    /**
     * 生成月维度时间点
     */
    private List<String> generateMonthlyPoints(String year) {
        List<String> months = new ArrayList<>();
        try {
            int yearInt = Integer.parseInt(year);
            for (int month = 1; month <= 12; month++) {
                months.add(yearInt + "-" + (month < 10 ? "0" : "") + month);
            }
        } catch (Exception e) {
            months.add(year);
        }
        return months;
    }

    /**
     * 生成小时维度时间点
     */
    private List<String> generateHourlyPoints(String date) {
        List<String> hours = new ArrayList<>();
        try {
            String[] parts = date.split("-");
            int year = Integer.parseInt(parts[0]);
            int month = Integer.parseInt(parts[1]);
            int day = Integer.parseInt(parts[2]);

            for (int hour = 0; hour < 24; hour++) {
                hours.add(year + "-" + (month < 10 ? "0" : "") + month + "-" + (day < 10 ? "0" : "") + day + " " + (hour < 10 ? "0" : "") + hour + ":00:00");
            }
        } catch (Exception e) {
            hours.add(date);
        }
        return hours;
    }

    /**
     * 计算单个时间点的趋势数据
     */
    private EmsTrendItemVO calculateTrendItem(List<String> deviceIds, String timeDimension, String timePoint, Long energyTypeId) {
        EmsTrendItemVO item = new EmsTrendItemVO();
        item.setTimeLabel(timePoint);

        try {
            // 查询设备能耗数据
            BigDecimal totalUsage = queryDeviceEnergyConsumption(deviceIds, timeDimension, timePoint, energyTypeId);

            // 计算标准煤和碳排放
            BigDecimal standardCoal = BigDecimal.ZERO;
            BigDecimal carbonEmission = BigDecimal.ZERO;

            if (energyTypeId != null && totalUsage != null && totalUsage.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal coalFactor = STANDARD_COAL_FACTORS.getOrDefault(energyTypeId, BigDecimal.ZERO);
                BigDecimal carbonFactor = CARBON_EMISSION_FACTORS.getOrDefault(energyTypeId, BigDecimal.ZERO);

                standardCoal = totalUsage.multiply(coalFactor).setScale(2, RoundingMode.HALF_UP);
                carbonEmission = totalUsage.multiply(carbonFactor).setScale(2, RoundingMode.HALF_UP);
            }

            item.setUsage(totalUsage != null ? totalUsage.setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO);
            item.setStandardCoal(standardCoal);
            item.setCarbonEmission(carbonEmission);

        } catch (Exception e) {
            // 出错时设置默认值
            item.setUsage(BigDecimal.ZERO);
            item.setStandardCoal(BigDecimal.ZERO);
            item.setCarbonEmission(BigDecimal.ZERO);
        }

        return item;
    }

    /**
     * 查询设备能耗消耗量
     */
    private BigDecimal queryDeviceEnergyConsumption(List<String> deviceIds, String timeDimension, String timePoint, Long energyTypeId) {
        BigDecimal totalUsage = BigDecimal.ZERO;

        System.out.println("Querying energy for timePoint: " + timePoint + ", deviceIds: " + deviceIds + ", energyTypeId: " + energyTypeId);

        try (Connection conn = mysqlDataSource.getConnection()) {
            String sql = buildEnergyQuerySQL(deviceIds, timeDimension, energyTypeId != null);

            System.out.println("Time range for " + timePoint + ": " + String.join(" | ", getTimeRange(timeDimension, timePoint)));

            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                int paramIndex = 1;

                // 设置设备ID参数
                System.out.println("Setting device IDs:");
                for (String deviceId : deviceIds) {
                    System.out.println("  - " + deviceId);
                    stmt.setString(paramIndex++, deviceId);
                }

                // 设置时间参数
                String[] timeRange = getTimeRange(timeDimension, timePoint);
                System.out.println("Setting time range: start=" + timeRange[0] + ", end=" + timeRange[1]);
                stmt.setString(paramIndex++, timeRange[0]);
                stmt.setString(paramIndex++, timeRange[1]);

                if (energyTypeId != null) {
                    System.out.println("Setting energyTypeId: " + energyTypeId);
                    stmt.setLong(paramIndex++, energyTypeId);
                }

                try (ResultSet rs = stmt.executeQuery()) {
                    if (rs.next()) {
                        double usage = rs.getDouble("total_usage");
                        if (!rs.wasNull()) {
                            totalUsage = new BigDecimal(usage);
                            System.out.println("Found usage: " + totalUsage + " for timePoint: " + timePoint);
                        } else {
                            System.out.println("No usage data (wasNull=true) for timePoint: " + timePoint);
                        }
                    } else {
                        System.out.println("No result set for timePoint: " + timePoint);
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error querying energy consumption for timePoint: " + timePoint);
            e.printStackTrace();
        }

        System.out.println("Final totalUsage for " + timePoint + ": " + totalUsage);
        return totalUsage;
    }

    /**
     * 构建能耗查询SQL
     */
    private String buildEnergyQuerySQL(List<String> deviceIds, String timeDimension, boolean hasEnergyTypeFilter) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT SUM(eed.end_value - eed.start_value) as total_usage ");
        sql.append("FROM ems_energy_consumption_device_data eed ");

        // 如果需要能源类型过滤，关联设备表和产品能源类型表
        if (hasEnergyTypeFilter) {
            sql.append("INNER JOIN ems_device ed ON eed.device_id = ed.id ");
            sql.append("INNER JOIN ems_product_energy_type epe ON ed.product_id = epe.product_id ");
        }

        sql.append("WHERE eed.device_id IN (");

        // 为每个设备ID添加占位符
        for (int i = 0; i < deviceIds.size(); i++) {
            if (i > 0) {
                sql.append(", ");
            }
            sql.append("?");
        }
        sql.append(") ");

        // 添加时间条件
        sql.append("AND eed.start_time >= ? AND eed.end_time <= ? ");

        if (hasEnergyTypeFilter) {
            sql.append("AND epe.energy_type = ? ");
        }

        System.out.println("Generated SQL: " + sql.toString());

        return sql.toString();
    }

    /**
     * 获取时间范围
     */
    private String[] getTimeRange(String timeDimension, String timePoint) {
        String[] range = new String[2];
        try {
            switch (timeDimension.toUpperCase()) {
                case "D":
                    // 对于小时级数据，timePoint已经包含具体时间，格式为 "YYYY-MM-DD HH:00:00"
                    if (timePoint.contains(" ")) {
                        // 提取日期和时间部分，格式：YYYY-MM-DD HH
                        String datePart = timePoint.split(" ")[0];
                        String hourPart = timePoint.split(" ")[1].split(":")[0];

                        // 设置整小时范围：HH:00:00 到 HH:59:59
                        range[0] = datePart + " " + hourPart + ":00:00";
                        range[1] = datePart + " " + hourPart + ":59:59";
                    } else {
                        // 如果没有时间，默认为全天
                        range[0] = timePoint + " 00:00:00";
                        range[1] = timePoint + " 23:59:59";
                    }
                    break;
                case "M":
                    range[0] = timePoint + " 00:00:00";
                    range[1] = timePoint + " 23:59:59";
                    break;
                case "Y":
                    // For yearly dimension, timePoint is in format "YYYY-MM", need full month
                    String[] yearMonth = timePoint.split("-");
                    if (yearMonth.length == 2) {
                        String yearPart = yearMonth[0];
                        String monthPart = yearMonth[1];
                        range[0] = yearPart + "-" + monthPart + "-01 00:00:00";

                        // Calculate last day of the month properly
                        java.time.YearMonth ym = java.time.YearMonth.of(Integer.parseInt(yearPart), Integer.parseInt(monthPart));
                        int lastDay = ym.lengthOfMonth();
                        range[1] = yearPart + "-" + monthPart + "-" + lastDay + " 23:59:59";
                    } else {
                        range[0] = timePoint + "-01-01 00:00:00";
                        range[1] = timePoint + "-12-31 23:59:59";
                    }
                    break;
                default:
                    range[0] = timePoint;
                    range[1] = timePoint;
            }
        } catch (Exception e) {
            range[0] = timePoint;
            range[1] = timePoint;
        }

        return range;
    }

    /**
     * 根据项目ID获取设备列表
     */
    private List<String> getDeviceIdsByProject(Long projectId, Long energyTypeId) {
        List<String> deviceIds = new ArrayList<>();
        try (Connection conn = mysqlDataSource.getConnection()) {
            StringBuilder sql = new StringBuilder();
            sql.append("SELECT DISTINCT ed.id ");
            sql.append("FROM ems_device ed ");

            if (energyTypeId != null) {
                sql.append("INNER JOIN ems_product_energy_type epe ON ed.product_id = epe.product_id ");
            }

            sql.append("WHERE 1=1 ");

            if (projectId != null) {
                sql.append("AND ed.project_id = ? ");
            }

            if (energyTypeId != null) {
                sql.append("AND epe.energy_type = ? ");
            }

            try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) {
                int paramIndex = 1;

                if (projectId != null) {
                    System.out.println("Setting projectId parameter: " + projectId);
                    stmt.setLong(paramIndex++, projectId);
                }

                if (energyTypeId != null) {
                    System.out.println("Setting energyTypeId parameter: " + energyTypeId);
                    stmt.setLong(paramIndex++, energyTypeId);
                }

                System.out.println("Device query SQL: " + sql.toString());

                try (ResultSet rs = stmt.executeQuery()) {
                    while (rs.next()) {
                        String deviceId = rs.getString("id");
                        deviceIds.add(deviceId);
                        System.out.println("Found device ID: " + deviceId);
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error getting device IDs: " + e.getMessage());
            e.printStackTrace();
        }

        System.out.println("Total device IDs found: " + deviceIds.size());
        return deviceIds;
    }

    @Override
    public EmsTrendIndicatorsResponse getTrendIndicators(Long projectId, String timeDimension, String timeValue, Long energyTypeId) {
        EmsTrendIndicatorsResponse resp = new EmsTrendIndicatorsResponse();
        resp.setTimeDimension(timeDimension);
        resp.setTimeValue(timeValue);

        try {
            // 获取项目下的所有设备
            List<String> deviceIds = getDeviceIdsByProject(projectId, energyTypeId);
            if (deviceIds.isEmpty()) {
                return resp;
            }

            // 计算总用量
            String[] timeRange = getTimeRange(timeDimension, timeValue);
            BigDecimal totalUsage = queryTotalEnergyConsumption(deviceIds, timeRange[0], timeRange[1], energyTypeId);

            // 计算标准煤和碳排放
            BigDecimal standardCoal = BigDecimal.ZERO;
            BigDecimal carbonEmission = BigDecimal.ZERO;

            if (energyTypeId != null && totalUsage.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal coalFactor = STANDARD_COAL_FACTORS.getOrDefault(energyTypeId, BigDecimal.ZERO);
                BigDecimal carbonFactor = CARBON_EMISSION_FACTORS.getOrDefault(energyTypeId, BigDecimal.ZERO);

                standardCoal = totalUsage.multiply(coalFactor).setScale(2, RoundingMode.HALF_UP);
                carbonEmission = totalUsage.multiply(carbonFactor).setScale(2, RoundingMode.HALF_UP);
            }

            // 计算单位面积能耗和人均能耗
            Map<String, BigDecimal> areaAndPopulation = getProjectAreaAndPopulation(projectId);
            BigDecimal totalArea = areaAndPopulation.get("area");
            BigDecimal totalPopulation = areaAndPopulation.get("population");

            BigDecimal areaUsage = BigDecimal.ZERO;
            BigDecimal perCapitaUsage = BigDecimal.ZERO;

            if (totalArea.compareTo(BigDecimal.ZERO) > 0) {
                areaUsage = totalUsage.divide(totalArea, 2, RoundingMode.HALF_UP);
            }

            if (totalPopulation.compareTo(BigDecimal.ZERO) > 0) {
                perCapitaUsage = totalUsage.divide(totalPopulation, 2, RoundingMode.HALF_UP);
            }

            // 评定等级
            Map<String, String> rating = calculateRating(areaUsage, energyTypeId);

            resp.setTotalUsage(totalUsage.setScale(2, RoundingMode.HALF_UP));
            resp.setStandardCoal(standardCoal);
            resp.setCarbonEmission(carbonEmission);
            resp.setAreaUsage(areaUsage);
            resp.setPerCapitaUsage(perCapitaUsage);
            resp.setRating(rating);

        } catch (Exception e) {
            System.err.println("Error getting trend indicators: " + e.getMessage());
        }

        return resp;
    }

    /**
     * 查询总能耗
     */
    private BigDecimal queryTotalEnergyConsumption(List<String> deviceIds, String startTime, String endTime, Long energyTypeId) {
        BigDecimal totalUsage = BigDecimal.ZERO;

        try (Connection conn = mysqlDataSource.getConnection()) {
            StringBuilder sql = new StringBuilder();
            sql.append("SELECT SUM(eed.end_value - eed.start_value) as total_usage ");
            sql.append("FROM ems_energy_consumption_device_data eed ");
            if (energyTypeId != null) {
                sql.append("INNER JOIN ems_device ed ON eed.device_id = ed.id ");
                sql.append("INNER JOIN ems_product_energy_type epe ON ed.product_id = epe.product_id ");
            }
            sql.append("WHERE eed.device_id IN (")
                    .append(String.join(",", Collections.nCopies(deviceIds.size(), "?")))
                    .append(") ");
            sql.append("AND eed.start_time >= ? AND eed.end_time <= ? ");

            if (energyTypeId != null) {
                sql.append("AND epe.energy_type = ? ");
            }

            try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) {
                int paramIndex = 1;

                // 设置设备ID参数
                for (String deviceId : deviceIds) {
                    stmt.setString(paramIndex++, deviceId);
                }

                // 设置时间参数
                stmt.setString(paramIndex++, startTime);
                stmt.setString(paramIndex++, endTime);

                if (energyTypeId != null) {
                    stmt.setLong(paramIndex++, energyTypeId);
                }

                try (ResultSet rs = stmt.executeQuery()) {
                    if (rs.next()) {
                        double usage = rs.getDouble("total_usage");
                        if (!rs.wasNull()) {
                            totalUsage = new BigDecimal(usage);
                        }
                    } else {
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error querying total energy consumption: " + e.getMessage());
        }

        return totalUsage;
    }

    /**
     * 获取项目总面积和总人数
     */
    private Map<String, BigDecimal> getProjectAreaAndPopulation(Long projectId) {
        Map<String, BigDecimal> result = new HashMap<>();
        result.put("area", BigDecimal.ZERO);
        result.put("population", BigDecimal.ZERO);

        if (projectId == null) {
            return result;
        }

        System.out.println("Getting project area and population for projectId: " + projectId);

        try (Connection conn = mysqlDataSource.getConnection()) {
            String sql = "SELECT COALESCE(SUM(ba.area),0) as total_area, COALESCE(SUM(ba.resident_population),0) as total_population " +
                    "FROM base_area ba " +
                    "INNER JOIN base_space_area bsa ON bsa.area_id = ba.id " +
                    "INNER JOIN base_space bs ON bs.id = bsa.space_id " +
                    "WHERE bs.root_id = (SELECT space_id FROM ems_project WHERE id = ?)";

            System.out.println("Project area SQL: " + sql);
            System.out.println("Setting projectId parameter: " + projectId);

            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setLong(1, projectId);

                try (ResultSet rs = stmt.executeQuery()) {
                    if (rs.next()) {
                        double area = rs.getDouble("total_area");
                        double population = rs.getDouble("total_population");

                        result.put("area", rs.wasNull() || Double.isNaN(area) ? BigDecimal.ZERO : new BigDecimal(area));
                        result.put("population", rs.wasNull() || Double.isNaN(population) ? BigDecimal.ZERO : new BigDecimal(population));
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error getting project area and population: " + e.getMessage());
        }

        return result;
    }

    /**
     * 计算能耗评定等级
     */
    private Map<String, String> calculateRating(BigDecimal areaUsage, Long energyTypeId) {
        Map<String, String> rating = new HashMap<>();

        // 简单的评定逻辑，可以根据实际需求调整
        String level;
        String description = "单位面积能耗评定";

        if (energyTypeId == null) {
            level = "良好";
        } else {
            switch (energyTypeId.intValue()) {
                case 1: // 电
                    if (areaUsage.compareTo(new BigDecimal("10")) < 0) {
                        level = "优秀";
                    } else if (areaUsage.compareTo(new BigDecimal("20")) < 0) {
                        level = "良好";
                    } else if (areaUsage.compareTo(new BigDecimal("30")) < 0) {
                        level = "一般";
                    } else {
                        level = "较差";
                    }
                    break;
                case 2: // 水
                    if (areaUsage.compareTo(new BigDecimal("2")) < 0) {
                        level = "优秀";
                    } else if (areaUsage.compareTo(new BigDecimal("5")) < 0) {
                        level = "良好";
                    } else if (areaUsage.compareTo(new BigDecimal("8")) < 0) {
                        level = "一般";
                    } else {
                        level = "较差";
                    }
                    break;
                case 3: // 气
                    if (areaUsage.compareTo(new BigDecimal("5")) < 0) {
                        level = "优秀";
                    } else if (areaUsage.compareTo(new BigDecimal("10")) < 0) {
                        level = "良好";
                    } else if (areaUsage.compareTo(new BigDecimal("15")) < 0) {
                        level = "一般";
                    } else {
                        level = "较差";
                    }
                    break;
                default:
                    level = "良好";
            }
        }

        rating.put("level", level);
        rating.put("description", description);

        return rating;
    }

    /**
     * 获取所有能源类型
     */
    private List<Long> getAllEnergyTypes(Long specificEnergyType) {
        List<Long> energyTypes = new ArrayList<>();

        if (specificEnergyType != null) {
            energyTypes.add(specificEnergyType);
        } else {
            // 返回所有支持的能源类型（1:电, 2:水, 3:气）
            energyTypes.addAll(Arrays.asList(1L, 2L, 3L));
        }

        return energyTypes;
    }

    /**
     * 解析区域ID
     */
    private List<Long> parseRegionIds(String regionIds, Long projectId) {
        List<Long> regionIdList = new ArrayList<>();

        if (regionIds != null && !regionIds.trim().isEmpty()) {
            // 解析逗号分隔的区域ID
            try {
                regionIdList = Arrays.stream(regionIds.split(","))
                        .map(String::trim)
                        .map(new java.util.function.Function<String, Long>() {
                            @Override
                            public Long apply(String s) {
                                return Long.valueOf(s);
                            }
                        })
                        .collect(Collectors.toList());
            } catch (Exception e) {
                System.err.println("Error parsing region IDs: " + e.getMessage());
                return regionIdList;
            }
        } else if (projectId != null) {
            // 如果没有指定区域ID，则获取项目下的所有区域
            try (Connection conn = mysqlDataSource.getConnection()) {
                // 查找该项目下的所有区域（type=2表示区域类型）
                String sql = "SELECT id FROM base_space WHERE type = 2 AND root_id = " +
                        "(SELECT space_id FROM ems_project WHERE id = ?)";

                try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                    stmt.setLong(1, projectId);

                    try (ResultSet rs = stmt.executeQuery()) {
                        while (rs.next()) {
                            regionIdList.add(Long.valueOf(rs.getLong("id")));
                        }
                    }
                }
            } catch (Exception e) {
                System.err.println("Error getting region IDs by project: " + e.getMessage());
            }
        }

        return regionIdList;
    }

    /**
     * 分析区域能耗情况
     */
    private EmsRegionAnalysisItemVO analyzeRegionEnergyConsumption(Long regionId, String startTime, String endTime,
                                                                   Long energyTypeId, String timeDimension, String timeValue) {
        try (Connection conn = mysqlDataSource.getConnection()) {
            // 获取区域基本信息
            String regionName = getRegionName(regionId, conn);
            if (regionName == null) {
                return null;
            }

            // 获取区域面积
            BigDecimal area = getRegionArea(regionId, conn);

            // 获取该区域下的设备能耗
            List<String> deviceIds = getDeviceIdsByRegion(regionId, energyTypeId, conn);
            BigDecimal totalUsage = BigDecimal.ZERO;
            String unit = "kWh";

            if (!deviceIds.isEmpty()) {
                totalUsage = queryTotalEnergyConsumption(deviceIds, startTime, endTime, energyTypeId);
                if (energyTypeId != null) {
                    unit = ENERGY_UNITS.getOrDefault(energyTypeId, "kWh");
                }
            }

            // 计算单位面积能耗
            BigDecimal areaUsage = BigDecimal.ZERO;
            if (area.compareTo(BigDecimal.ZERO) > 0) {
                areaUsage = totalUsage.divide(area, 2, RoundingMode.HALF_UP);
            }

            EmsRegionAnalysisItemVO item = new EmsRegionAnalysisItemVO();
            item.setRegionId(regionId);
            item.setRegionName(regionName);
            item.setArea(area.setScale(2, RoundingMode.HALF_UP));
            item.setTotalUsage(totalUsage.setScale(2, RoundingMode.HALF_UP));
            item.setAreaUsage(areaUsage);
            item.setUnit(unit);
            item.setTimeDimension(timeDimension);
            item.setTimeValue(timeValue);

            return item;
        } catch (Exception e) {
            System.err.println("Error analyzing region energy consumption: " + e.getMessage());
            return null;
        }
    }

    /**
     * 获取区域名称
     */
    private String getRegionName(Long regionId, Connection conn) {
        try {
            String sql = "SELECT name FROM base_space WHERE id = ?";
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setLong(1, regionId);
                try (ResultSet rs = stmt.executeQuery()) {
                    if (rs.next()) {
                        return rs.getString("name");
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error getting region name: " + e.getMessage());
        }
        return "未知区域";
    }

    /**
     * 获取区域面积
     */
    private BigDecimal getRegionArea(Long regionId, Connection conn) {
        try {
            String sql = "SELECT COALESCE(SUM(ba.area),0) as total_area FROM base_area ba " +
                    "INNER JOIN base_space_area bsa ON bsa.area_id = ba.id WHERE bsa.space_id = ?";
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setLong(1, regionId);
                try (ResultSet rs = stmt.executeQuery()) {
                    if (rs.next()) {
                        double area = rs.getDouble("total_area");
                        if (!rs.wasNull()) {
                            return new BigDecimal(area);
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error getting region area: " + e.getMessage());
        }
        return BigDecimal.ZERO;
    }

    /**
     * 获取区域下的设备ID列表
     */
    private List<String> getDeviceIdsByRegion(Long regionId, Long energyTypeId, Connection conn) {
        List<String> deviceIds = new ArrayList<>();

        try {
            StringBuilder sql = new StringBuilder();
            sql.append("SELECT DISTINCT ed.id ");
            sql.append("FROM ems_device ed ");

            if (energyTypeId != null) {
                sql.append("INNER JOIN ems_product_energy_type epe ON ed.product_id = epe.product_id ");
            }

            // 查找该区域下的建筑，然后在这些建筑中查找设备
            sql.append("WHERE ed.monitoring_location IN ");
            sql.append("(SELECT build_id FROM base_space_build WHERE space_id = ?) ");

            if (energyTypeId != null) {
                sql.append("AND epe.energy_type = ? ");
            }

            try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) {
                int paramIndex = 1;
                stmt.setLong(paramIndex++, regionId);

                if (energyTypeId != null) {
                    stmt.setLong(paramIndex++, energyTypeId);
                }

                try (ResultSet rs = stmt.executeQuery()) {
                    while (rs.next()) {
                        deviceIds.add(rs.getString("id"));
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error getting device IDs by region: " + e.getMessage());
        }

        return deviceIds;
    }

    @Override
    public EmsTrendCategoryResponse getTrendCategory(Long projectId, String timeDimension, String timeValue, Long energyTypeId) {
        EmsTrendCategoryResponse resp = new EmsTrendCategoryResponse();
        resp.setTimeDimension(timeDimension);
        resp.setTimeValue(timeValue);

        try {
            // 获取所有能源类型
            List<Long> energyTypes = getAllEnergyTypes(energyTypeId);

            // 查询每个能源类型的用量
            List<EmsCategoryRatioItemVO> categoryItems = new ArrayList<>();
            BigDecimal totalAllUsage = BigDecimal.ZERO;
            Map<Long, BigDecimal> energyTypeUsage = new HashMap<>();

            for (Long typeId : energyTypes) {
                List<String> deviceIds = getDeviceIdsByProject(projectId, typeId);
                if (!deviceIds.isEmpty()) {
                    String[] timeRange = getTimeRange(timeDimension, timeValue);
                    BigDecimal usage = queryTotalEnergyConsumption(deviceIds, timeRange[0], timeRange[1], typeId);
                    energyTypeUsage.put(typeId, usage);
                    totalAllUsage = totalAllUsage.add(usage);
                } else {
                    energyTypeUsage.put(typeId, BigDecimal.ZERO);
                }
            }

            // 计算占比和构建响应
            for (Long typeId : energyTypes) {
                BigDecimal usage = energyTypeUsage.get(typeId);
                BigDecimal ratio = BigDecimal.ZERO;

                if (totalAllUsage.compareTo(BigDecimal.ZERO) > 0) {
                    ratio = usage.divide(totalAllUsage, 4, RoundingMode.HALF_UP)
                            .multiply(new BigDecimal("100"))
                            .setScale(1, RoundingMode.HALF_UP);
                }

                EmsCategoryRatioItemVO item = new EmsCategoryRatioItemVO();
                item.setEnergyTypeName(typeId >= 1 && typeId <= 3 ? ENERGY_TYPE_NAMES[typeId.intValue()] : "未知");
                item.setRatio(ratio);
                item.setUsage(usage.setScale(2, RoundingMode.HALF_UP));
                item.setUnit(ENERGY_UNITS.get(typeId));

                categoryItems.add(item);
            }

            resp.setCategoryRatio(categoryItems);

        } catch (Exception e) {
            System.err.println("Error getting trend category: " + e.getMessage());
        }

        return resp;
    }

    @Override
    public EmsRegionAnalysisResponse getRegionAnalysis(Long projectId, String regionIds, String timeDimension, String timeValue, Long energyTypeId) {
        EmsRegionAnalysisResponse resp = new EmsRegionAnalysisResponse();

        try {
            // 简化实现：如果没有指定区域ID，返回项目整体数据作为一个区域
            List<EmsRegionAnalysisItemVO> items = new ArrayList<>();
            String[] timeRange = getTimeRange(timeDimension, timeValue);

            // 获取项目下的所有设备
            List<String> deviceIds = getDeviceIdsByProject(projectId, energyTypeId);
            if (!deviceIds.isEmpty()) {
                // 计算项目总体能耗
                BigDecimal totalUsage = queryTotalEnergyConsumption(deviceIds, timeRange[0], timeRange[1], energyTypeId);

                // 获取项目面积
                Map<String, BigDecimal> areaAndPopulation = getProjectAreaAndPopulation(projectId);
                BigDecimal totalArea = areaAndPopulation.get("area");

                // 计算单位面积能耗
                BigDecimal areaUsage = BigDecimal.ZERO;
                if (totalArea.compareTo(BigDecimal.ZERO) > 0) {
                    areaUsage = totalUsage.divide(totalArea, 2, RoundingMode.HALF_UP);
                }

                // 设置单位
                String unit = "kWh";
                if (energyTypeId != null) {
                    unit = ENERGY_UNITS.getOrDefault(energyTypeId, "kWh");
                }

                EmsRegionAnalysisItemVO item = new EmsRegionAnalysisItemVO();
                item.setRegionId(projectId != null ? projectId : 1L);
                item.setRegionName(projectId != null ? "项目" + projectId : "默认项目");
                item.setArea(totalArea.setScale(2, RoundingMode.HALF_UP));
                item.setTotalUsage(totalUsage.setScale(2, RoundingMode.HALF_UP));
                item.setAreaUsage(areaUsage);
                item.setUnit(unit);
                item.setTimeDimension(timeDimension);
                item.setTimeValue(timeValue);

                items.add(item);
            }

            resp.setItems(items);

        } catch (Exception e) {
            System.err.println("Error getting region analysis: " + e.getMessage());
        }

        return resp;
    }

    @Override
    public EmsCompareResponse getCompare(EmsCompareRequest request) {
        EmsCompareResponse resp = new EmsCompareResponse();

        try {
            if (request == null || request.getDeviceIds() == null || request.getDeviceIds().isEmpty() ||
                    request.getTimeValues() == null || request.getTimeValues().isEmpty()) {
                return resp;
            }

            // 设置能源类型名称
            if (request.getEnergyTypeId() != null) {
                long id = request.getEnergyTypeId();
                resp.setEnergyTypeName(id >= 1 && id <= 3 ? ENERGY_TYPE_NAMES[(int) id] : "");
            }

            // 根据查询类型和起止时间生成完整时间轴
            List<String> timeSeries = generateCompareTimeSeries(request.getTimeDimension(), request.getTimeValues());
            resp.setTimeDimension(request.getTimeDimension());
            resp.setDimension(timeSeries);

            // 批量查询设备历史数据并按时间轴计算对比值
            List<EmsCompareSeriesItemVO> series = buildCompareSeries(
                    request.getDeviceIds(), request.getTimeDimension(), timeSeries);
            resp.setSeries(series);

        } catch (Exception e) {
            log.warn("获取对比数据异常: {}", e.getMessage(), e);
        }

        return resp;
    }

    /**
     * 根据查询类型（日/月/年）和起止时间，生成完整时间序列
     */
    private List<String> generateCompareTimeSeries(String timeDimension, List<String> timeValues) {
        if (timeValues == null || timeValues.isEmpty()) {
            return new ArrayList<>();
        }
        String start = timeValues.get(0).trim();
        String end = (timeValues.size() > 1 ? timeValues.get(timeValues.size() - 1) : start).trim();
        if (start.isEmpty()) {
            return new ArrayList<>();
        }
        if (end.isEmpty()) {
            end = start;
        }

        try {
            switch (timeDimension.toUpperCase()) {
                case "D":
                    return generateDailyRange(start, end);
                case "M":
                    return generateMonthlyRange(start, end);
                case "Y":
                    return generateYearlyRange(start, end);
                default:
                    return new ArrayList<>(timeValues);
            }
        } catch (Exception e) {
            log.warn("生成对比时间序列失败: timeDimension={}, timeValues={}", timeDimension, timeValues, e);
            return new ArrayList<>(timeValues);
        }
    }

    private List<String> generateDailyRange(String start, String end) {
        List<String> points = new ArrayList<>();
        LocalDate startDate = LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE);
        LocalDate endDate = LocalDate.parse(end, DateTimeFormatter.ISO_LOCAL_DATE);
        if (endDate.isBefore(startDate)) {
            LocalDate tmp = startDate;
            startDate = endDate;
            endDate = tmp;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE;
        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
            points.add(date.format(formatter));
        }
        return points;
    }

    private List<String> generateMonthlyRange(String start, String end) {
        List<String> points = new ArrayList<>();
        YearMonth startMonth = YearMonth.parse(start, DateTimeFormatter.ofPattern("yyyy-MM"));
        YearMonth endMonth = YearMonth.parse(end, DateTimeFormatter.ofPattern("yyyy-MM"));
        if (endMonth.isBefore(startMonth)) {
            YearMonth tmp = startMonth;
            startMonth = endMonth;
            endMonth = tmp;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        for (YearMonth month = startMonth; !month.isAfter(endMonth); month = month.plusMonths(1)) {
            points.add(month.format(formatter));
        }
        return points;
    }

    private List<String> generateYearlyRange(String start, String end) {
        List<String> points = new ArrayList<>();
        int startYear = Integer.parseInt(start);
        int endYear = Integer.parseInt(end);
        if (endYear < startYear) {
            int tmp = startYear;
            startYear = endYear;
            endYear = tmp;
        }
        for (int year = startYear; year <= endYear; year++) {
            points.add(String.valueOf(year));
        }
        return points;
    }

    /**
     * 批量查询设备对比数据：一次拉取 TSDB 历史数据，再按时间轴分段计算用量
     */
    private List<EmsCompareSeriesItemVO> buildCompareSeries(List<String> deviceIds, String timeDimension,
                                                            List<String> timeSeries) {
        if (deviceIds == null || deviceIds.isEmpty() || timeSeries == null || timeSeries.isEmpty()) {
            return Collections.emptyList();
        }

        Map<String, DmpDevice> deviceMap = loadCompareDevices(deviceIds);
        Set<String> deviceUuids = deviceMap.values().stream()
                .map(DmpDevice::getDeviceUuid)
                .filter(StringUtils::hasText)
                .map(String::trim)
                .collect(Collectors.toCollection(LinkedHashSet::new));

        Map<String, Map<String, TreeMap<LocalDateTime, Double>>> deviceMetricData = Collections.emptyMap();
        if (!deviceUuids.isEmpty()) {
            String[] queryRange = getOverallCompareQueryRange(timeDimension, timeSeries);
            deviceMetricData = queryDeviceMetricHistory(deviceUuids, queryRange[0], queryRange[1]);
        }

        List<AverageTimeSegment> segments = buildCompareTimeSegments(timeDimension, timeSeries);
        List<EmsCompareSeriesItemVO> series = new ArrayList<>();
        for (String deviceId : deviceIds) {
            DmpDevice device = deviceMap.get(deviceId);
            String deviceUuid = device != null && StringUtils.hasText(device.getDeviceUuid())
                    ? device.getDeviceUuid().trim() : null;
            TreeMap<LocalDateTime, Double> timeValueMap = null;
            if (deviceUuid != null) {
                Map<String, TreeMap<LocalDateTime, Double>> metricMap = deviceMetricData.get(deviceUuid);
                if (metricMap != null) {
                    timeValueMap = metricMap.get(ENERGY_METRIC);
                }
            }

            List<EmsCompareValueVO> values = new ArrayList<>();
            for (AverageTimeSegment segment : segments) {
                BigDecimal usage = BigDecimal.ZERO.setScale(USAGE_SCALE, RoundingMode.HALF_UP);
                if (timeValueMap != null && !timeValueMap.isEmpty()) {
                    SegmentUsageResult usageResult = calculateSegmentUsage(
                            timeValueMap, segment.getStartInclusive(), segment.getEndExclusive());
                    if (usageResult.isHasValidReading()) {
                        usage = BigDecimal.valueOf(usageResult.getUsage())
                                .setScale(USAGE_SCALE, RoundingMode.HALF_UP);
                    }
                }
                EmsCompareValueVO valueItem = new EmsCompareValueVO();
                valueItem.setTimeValue(segment.getLabel());
                valueItem.setUsage(usage);
                values.add(valueItem);
            }

            EmsCompareSeriesItemVO seriesItem = new EmsCompareSeriesItemVO();
            seriesItem.setDeviceId(deviceId);
            seriesItem.setDeviceName(device != null && StringUtils.hasText(device.getDeviceName())
                    ? device.getDeviceName() : "未知设备");
            seriesItem.setValues(values);
            series.add(seriesItem);
        }
        return series;
    }

    private Map<String, DmpDevice> loadCompareDevices(List<String> deviceIds) {
        List<DmpDevice> devices = dmpDeviceMapper.selectList(
                new LambdaQueryWrapper<DmpDevice>()
                        .in(DmpDevice::getDeviceId, deviceIds)
                        .eq(DmpDevice::getDeleteFlag, 0));
        Map<String, DmpDevice> deviceMap = new LinkedHashMap<>();
        for (DmpDevice device : devices) {
            if (device != null && StringUtils.hasText(device.getDeviceId())) {
                deviceMap.putIfAbsent(device.getDeviceId().trim(), device);
            }
        }
        return deviceMap;
    }

    private String[] getOverallCompareQueryRange(String timeDimension, List<String> timeSeries) {
        LocalDateTime[] firstSegment = resolveCompareSegmentRange(timeDimension, timeSeries.get(0));
        LocalDateTime[] lastSegment = resolveCompareSegmentRange(timeDimension, timeSeries.get(timeSeries.size() - 1));
        return new String[]{
                firstSegment[0].format(DATE_TIME_FORMATTER),
                lastSegment[1].minusSeconds(1).format(DATE_TIME_FORMATTER)
        };
    }

    private List<AverageTimeSegment> buildCompareTimeSegments(String timeDimension, List<String> timeSeries) {
        List<AverageTimeSegment> segments = new ArrayList<>();
        for (String timeValue : timeSeries) {
            LocalDateTime[] range = resolveCompareSegmentRange(timeDimension, timeValue);
            segments.add(new AverageTimeSegment(timeValue, range[0], range[1]));
        }
        return segments;
    }

    /**
     * 对比分析时间段：日=自然日，月=自然月，年=自然年
     */
    private LocalDateTime[] resolveCompareSegmentRange(String timeDimension, String timePoint) {
        switch (timeDimension.toUpperCase()) {
            case "D":
                LocalDate day = LocalDate.parse(timePoint, DateTimeFormatter.ISO_LOCAL_DATE);
                return new LocalDateTime[]{
                        day.atStartOfDay(),
                        day.plusDays(1).atStartOfDay()
                };
            case "M":
                YearMonth month = YearMonth.parse(timePoint, DateTimeFormatter.ofPattern("yyyy-MM"));
                return new LocalDateTime[]{
                        month.atDay(1).atStartOfDay(),
                        month.plusMonths(1).atDay(1).atStartOfDay()
                };
            case "Y":
                int year = Integer.parseInt(timePoint);
                return new LocalDateTime[]{
                        LocalDate.of(year, 1, 1).atStartOfDay(),
                        LocalDate.of(year + 1, 1, 1).atStartOfDay()
                };
            default:
                String[] range = getTimeRange(timeDimension, timePoint);
                return new LocalDateTime[]{
                        parseDateTime(range[0]),
                        parseDateTime(range[1]).plusSeconds(1)
                };
        }
    }

    @Override
    public EmsAverageResponse getAverage(Long projectId, String timeDimension, String timeValue, Long energyTypeId) {
        EmsAverageResponse resp = new EmsAverageResponse();
        resp.setDimension(timeDimension);
        resp.setTimeValue(timeValue);

        try {
            // 获取项目下的所有设备
            List<String> deviceIds = getDeviceIdsByProject(projectId, energyTypeId);
            if (deviceIds.isEmpty()) {
                return resp;
            }

            // 查询总能耗
            String[] timeRange = getTimeRange(timeDimension, timeValue);
            BigDecimal totalUsage = queryTotalEnergyConsumption(deviceIds, timeRange[0], timeRange[1], energyTypeId);

            // 获取项目面积和人口信息
            Map<String, BigDecimal> areaAndPopulation = getProjectAreaAndPopulation(projectId);
            BigDecimal totalArea = areaAndPopulation.get("area");
            BigDecimal totalPopulation = areaAndPopulation.get("population");

            // 计算单位面积能耗和人均能耗
            BigDecimal areaUsage = BigDecimal.ZERO;
            BigDecimal perCapitaUsage = BigDecimal.ZERO;

            if (totalArea.compareTo(BigDecimal.ZERO) > 0) {
                areaUsage = totalUsage.divide(totalArea, 2, RoundingMode.HALF_UP);
            }

            if (totalPopulation.compareTo(BigDecimal.ZERO) > 0) {
                perCapitaUsage = totalUsage.divide(totalPopulation, 2, RoundingMode.HALF_UP);
            }

            // 设置单位
            String unit = "kWh";
            if (energyTypeId != null) {
                unit = ENERGY_UNITS.getOrDefault(energyTypeId, "kWh");
            }

            resp.setAreaUsage(areaUsage);
            resp.setPerCapitaUsage(perCapitaUsage);
            resp.setUnit(unit);

        } catch (Exception e) {
            System.err.println("Error getting average data: " + e.getMessage());
        }

        return resp;
    }

    @Override
    public EmsAverageResponseVO getRegionalAnalysis(EmsAverageRequest request) {
        EmsAverageResponseVO resp = new EmsAverageResponseVO();
        if (request == null || !StringUtils.hasText(request.getCode())
                || request.getSpaces() == null || request.getSpaces().isEmpty()) {
            return resp;
        }

        LocalDateTime startTime = parseDateTime(request.getStartTime());
        LocalDateTime endTime = parseDateTime(request.getEndTime());
        if (startTime == null || endTime == null || !startTime.isBefore(endTime)) {
            return resp;
        }

        String dateType = request.getDateType() != null ? request.getDateType().toLowerCase() : "hour";
        List<AverageTimeSegment> segments = buildAverageTimeSegments(dateType, startTime, endTime);
        if (segments.isEmpty()) {
            return resp;
        }

        resp.setTimeData(segments.stream().map(AverageTimeSegment::getLabel).collect(Collectors.toList()));

        Set<String> itemCodeDevices = resolveItemCodeDeviceUuids(request.getCode());
        if (itemCodeDevices.isEmpty()) {
            resp.setDataList(buildEmptySpaceDataList(request.getSpaces(), segments));
            return resp;
        }

        Map<Long, List<String>> spaceDeviceMap = new LinkedHashMap<>();
        Set<String> allDeviceUuids = new LinkedHashSet<>();
        for (EmsAverageRequest.SpaceItem space : request.getSpaces()) {
            if (space == null || space.getId() == null) {
                continue;
            }
            List<String> spaceDevices = resolveSpaceDeviceUuids(space.getId());
            List<String> intersected = spaceDevices.stream()
                    .filter(itemCodeDevices::contains)
                    .distinct()
                    .collect(Collectors.toList());
            spaceDeviceMap.put(space.getId(), intersected);
            allDeviceUuids.addAll(intersected);
        }

        Map<String, Map<String, TreeMap<LocalDateTime, Double>>> deviceMetricData = Collections.emptyMap();
        if (!allDeviceUuids.isEmpty()) {
            deviceMetricData = queryDeviceMetricHistory(allDeviceUuids, request.getStartTime(), request.getEndTime());
        }

        boolean perAreaQuery = isPerAreaQuery(request.getType());
        Map<Long, BigDecimal> spaceAreaMap = perAreaQuery
                ? resolveSpaceBuildAreaMap(request.getSpaces())
                : Collections.emptyMap();

        List<EmsAverageSpaceItemVO> dataList = new ArrayList<>();
        for (EmsAverageRequest.SpaceItem space : request.getSpaces()) {
            if (space == null || space.getId() == null) {
                continue;
            }
            EmsAverageSpaceItemVO spaceItem = new EmsAverageSpaceItemVO();
            spaceItem.setSpaceId(space.getId());
            spaceItem.setName(space.getName());

            List<String> deviceUuids = spaceDeviceMap.getOrDefault(space.getId(), Collections.emptyList());
            BigDecimal buildArea = perAreaQuery ? spaceAreaMap.get(space.getId()) : null;
            boolean invalidArea = perAreaQuery && !isValidBuildArea(buildArea);

            List<EmsAveragePointVO> points = new ArrayList<>();
            for (AverageTimeSegment segment : segments) {
                EmsAveragePointVO point = new EmsAveragePointVO();
                point.setTime(segment.getLabel());
                if (invalidArea || deviceUuids.isEmpty()) {
                    point.setValue(ENERGY_NO_DATA);
                } else {
                    String usageValue = calculateDevicesSegmentUsage(
                            deviceUuids, deviceMetricData, segment.getStartInclusive(), segment.getEndExclusive());
                    point.setValue(perAreaQuery
                            ? toPerAreaValue(usageValue, buildArea)
                            : usageValue);
                }
                points.add(point);
            }
            spaceItem.setList(points);
            dataList.add(spaceItem);
        }

        resp.setDataList(dataList);
        return resp;
    }

    private List<EmsAverageSpaceItemVO> buildEmptySpaceDataList(List<EmsAverageRequest.SpaceItem> spaces,
                                                                List<AverageTimeSegment> segments) {
        List<EmsAverageSpaceItemVO> dataList = new ArrayList<>();
        for (EmsAverageRequest.SpaceItem space : spaces) {
            if (space == null || space.getId() == null) {
                continue;
            }
            EmsAverageSpaceItemVO spaceItem = new EmsAverageSpaceItemVO();
            spaceItem.setSpaceId(space.getId());
            spaceItem.setName(space.getName());
            for (AverageTimeSegment segment : segments) {
                EmsAveragePointVO point = new EmsAveragePointVO();
                point.setTime(segment.getLabel());
                point.setValue(ENERGY_NO_DATA);
                spaceItem.getList().add(point);
            }
            dataList.add(spaceItem);
        }
        return dataList;
    }

    private boolean isPerAreaQuery(Integer type) {
        return type != null && type == AVERAGE_TYPE_PER_AREA;
    }

    private boolean isValidBuildArea(BigDecimal area) {
        return area != null && area.compareTo(BigDecimal.ZERO) > 0;
    }

    /**
     * 按空间汇总关联建筑的建筑面积（base_space_build → base_build.build_area）
     */
    private Map<Long, BigDecimal> resolveSpaceBuildAreaMap(List<EmsAverageRequest.SpaceItem> spaces) {
        List<Long> spaceIds = spaces.stream()
                .filter(space -> space != null && space.getId() != null)
                .map(EmsAverageRequest.SpaceItem::getId)
                .distinct()
                .collect(Collectors.toList());
        if (spaceIds.isEmpty()) {
            return Collections.emptyMap();
        }

        List<BaseSpaceBuild> links = baseSpaceBuildMapper.selectList(
                new LambdaQueryWrapper<BaseSpaceBuild>().in(BaseSpaceBuild::getSpaceId, spaceIds));
        if (links == null || links.isEmpty()) {
            return Collections.emptyMap();
        }

        Set<Integer> buildIds = links.stream()
                .map(BaseSpaceBuild::getBuildId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (buildIds.isEmpty()) {
            return Collections.emptyMap();
        }

        List<BaseBuild> builds = baseBuildMapper.selectBatchIds(buildIds);
        Map<Integer, BigDecimal> buildAreaMap = new HashMap<>();
        if (builds != null) {
            for (BaseBuild build : builds) {
                if (build == null || build.getId() == null) {
                    continue;
                }
                if (build.getDeleteFlag() != null && build.getDeleteFlag() != 0) {
                    continue;
                }
                buildAreaMap.put(build.getId(), toBuildArea(build.getBuildArea()));
            }
        }

        Map<Long, BigDecimal> spaceAreaMap = new HashMap<>();
        for (BaseSpaceBuild link : links) {
            if (link == null || link.getSpaceId() == null || link.getBuildId() == null) {
                continue;
            }
            BigDecimal area = buildAreaMap.get(link.getBuildId());
            if (!isValidBuildArea(area)) {
                continue;
            }
            spaceAreaMap.merge(link.getSpaceId(), area, BigDecimal::add);
        }
        return spaceAreaMap;
    }

    private BigDecimal toBuildArea(Double buildArea) {
        if (buildArea == null || buildArea.isNaN() || buildArea <= 0) {
            return null;
        }
        return BigDecimal.valueOf(buildArea);
    }

    private String toPerAreaValue(String usageValue, BigDecimal buildArea) {
        if (ENERGY_NO_DATA.equals(usageValue) || !isValidBuildArea(buildArea)) {
            return ENERGY_NO_DATA;
        }
        try {
            BigDecimal usage = new BigDecimal(usageValue);
            return usage.divide(buildArea, USAGE_SCALE, RoundingMode.HALF_UP).toPlainString();
        } catch (NumberFormatException e) {
            return ENERGY_NO_DATA;
        }
    }

    /**
     * 按分项编码查询关联设备 UUID
     */
    private Set<String> resolveItemCodeDeviceUuids(String code) {
        Integer tenantId = SecurityUtils.getTenantId();
        List<String> deviceUuids = emsDeviceItemCodeMapper.selectDeviceUuidsByItemCode(tenantId, code.trim());
        if (deviceUuids == null || deviceUuids.isEmpty()) {
            return Collections.emptySet();
        }
        return deviceUuids.stream()
                .filter(StringUtils::hasText)
                .map(String::trim)
                .collect(Collectors.toCollection(LinkedHashSet::new));
    }

    /**
     * 按区域查询网关下所有设备 UUID
     */
    private List<String> resolveSpaceDeviceUuids(Long spaceId) {
        List<BaseSpaceGateway> gateways = baseSpaceGatewayMapper.selectList(
                new LambdaQueryWrapper<BaseSpaceGateway>().eq(BaseSpaceGateway::getSpaceId, spaceId));
        if (gateways == null || gateways.isEmpty()) {
            return Collections.emptyList();
        }

        Set<String> deviceUuids = new LinkedHashSet<>();
        for (BaseSpaceGateway gateway : gateways) {
            if (gateway == null || !StringUtils.hasText(gateway.getGatewayUuid())) {
                continue;
            }
            List<DmpDevice> devices = dmpDeviceMapper.selectDevicesByGatewayUuid(gateway.getGatewayUuid().trim());
            if (devices == null) {
                continue;
            }
            for (DmpDevice device : devices) {
                if (device != null && StringUtils.hasText(device.getDeviceUuid())) {
                    deviceUuids.add(device.getDeviceUuid().trim());
                }
            }
        }
        return new ArrayList<>(deviceUuids);
    }

    private Map<String, Map<String, TreeMap<LocalDateTime, Double>>> queryDeviceMetricHistory(
            Set<String> deviceUuids, String startTime, String endTime) {
        HistorysInnerRequestVO historyRequest = new HistorysInnerRequestVO();
        historyRequest.setDeviceuuid(new ArrayList<>(deviceUuids));
        historyRequest.setStartTime(startTime);
        historyRequest.setEndTime(endTime);
        historyRequest.setMetrics(Collections.singletonList(ENERGY_METRIC));

        ApiResult<List<HistorysInnerResultVO>> historyResult =
                remoteTsdbProxyService.queryHistoryDeviceData(historyRequest);
        if (historyResult == null || !historyResult.isSuccess()
                || historyResult.getData() == null || historyResult.getData().isEmpty()) {
            return Collections.emptyMap();
        }
        return parseDeviceMetricTimeValueMap(historyResult.getData());
    }

    private String calculateDevicesSegmentUsage(List<String> deviceUuids,
                                                Map<String, Map<String, TreeMap<LocalDateTime, Double>>> deviceMetricData,
                                                LocalDateTime startInclusive,
                                                LocalDateTime endExclusive) {
        BigDecimal total = BigDecimal.ZERO;
        boolean hasValidReading = false;

        for (String deviceUuid : deviceUuids) {
            Map<String, TreeMap<LocalDateTime, Double>> metricMap = deviceMetricData.get(deviceUuid);
            if (metricMap == null) {
                continue;
            }
            TreeMap<LocalDateTime, Double> timeValueMap = metricMap.get(ENERGY_METRIC);
            if (timeValueMap == null || timeValueMap.isEmpty()) {
                continue;
            }
            SegmentUsageResult usageResult = calculateSegmentUsage(timeValueMap, startInclusive, endExclusive);
            if (usageResult.isHasValidReading()) {
                hasValidReading = true;
                total = total.add(BigDecimal.valueOf(usageResult.getUsage()));
            }
        }

        if (!hasValidReading) {
            return ENERGY_NO_DATA;
        }
        return total.setScale(USAGE_SCALE, RoundingMode.HALF_UP).toPlainString();
    }

    private SegmentUsageResult calculateSegmentUsage(TreeMap<LocalDateTime, Double> timeValueMap,
                                                     LocalDateTime startInclusive,
                                                     LocalDateTime endExclusive) {
        TreeMap<LocalDateTime, Double> segmentData = new TreeMap<>();
        for (Map.Entry<LocalDateTime, Double> entry : timeValueMap.entrySet()) {
            LocalDateTime ts = entry.getKey();
            if (!ts.isBefore(startInclusive) && ts.isBefore(endExclusive)) {
                segmentData.put(ts, entry.getValue());
            }
        }
        if (segmentData.size() < 2) {
            return new SegmentUsageResult(false, 0);
        }
        double firstValue = segmentData.firstEntry().getValue();
        double lastValue = segmentData.lastEntry().getValue();
        double usage = lastValue - firstValue;
        if (usage < 0) {
            usage = lastValue;
        }
        return new SegmentUsageResult(true,
                BigDecimal.valueOf(usage).setScale(USAGE_SCALE, RoundingMode.HALF_UP).doubleValue());
    }

    private static class SegmentUsageResult {
        private final boolean hasValidReading;
        private final double usage;

        SegmentUsageResult(boolean hasValidReading, double usage) {
            this.hasValidReading = hasValidReading;
            this.usage = usage;
        }

        boolean isHasValidReading() {
            return hasValidReading;
        }

        double getUsage() {
            return usage;
        }
    }

    private Map<String, Map<String, TreeMap<LocalDateTime, Double>>> parseDeviceMetricTimeValueMap(
            List<HistorysInnerResultVO> historyDataList) {
        Map<String, Map<String, TreeMap<LocalDateTime, Double>>> deviceMetricTimeValueMap = new HashMap<>();
        if (historyDataList == null || historyDataList.isEmpty()) {
            return deviceMetricTimeValueMap;
        }

        for (HistorysInnerResultVO result : historyDataList) {
            if (result == null || !StringUtils.hasText(result.getDeviceuuid())) {
                continue;
            }
            String deviceUuid = result.getDeviceuuid();
            Map<String, TreeMap<LocalDateTime, Double>> metricTimeValueMap =
                    deviceMetricTimeValueMap.computeIfAbsent(deviceUuid, k -> new HashMap<>());

            if (result.getMetrics() == null) {
                continue;
            }
            for (MetricVO metric : result.getMetrics()) {
                if (metric == null || !StringUtils.hasText(metric.getMetric()) || metric.getMetricItems() == null) {
                    continue;
                }
                String metricName = metric.getMetric();
                TreeMap<LocalDateTime, Double> timeValueMap =
                        metricTimeValueMap.computeIfAbsent(metricName, k -> new TreeMap<>());

                for (Map<String, Object> point : metric.getMetricItems()) {
                    if (point == null || !point.containsKey("timestamp") || !point.containsKey("value")) {
                        continue;
                    }
                    try {
                        String timestampStr = point.get("timestamp").toString();
                        if (timestampStr.endsWith(".0")) {
                            timestampStr = timestampStr.substring(0, timestampStr.length() - 2);
                        }
                        LocalDateTime timestamp = LocalDateTime.parse(timestampStr, DATE_TIME_FORMATTER);
                        Double value = Double.parseDouble(point.get("value").toString());
                        timeValueMap.put(timestamp, value);
                    } catch (Exception ignored) {
                        // skip invalid point
                    }
                }
            }
        }
        return deviceMetricTimeValueMap;
    }

    private List<AverageTimeSegment> buildAverageTimeSegments(String dateType,
                                                              LocalDateTime startTime,
                                                              LocalDateTime endTime) {
        List<AverageTimeSegment> segments = new ArrayList<>();
        switch (dateType) {
            case "hour":
                buildHourAverageSegments(segments, startTime, endTime);
                break;
            case "day":
                buildDayAverageSegments(segments, startTime, endTime);
                break;
            case "month":
                buildMonthAverageSegments(segments, startTime, endTime);
                break;
            case "year":
                buildYearAverageSegments(segments, startTime, endTime);
                break;
            default:
                buildHourAverageSegments(segments, startTime, endTime);
                break;
        }
        return segments;
    }

    private void buildHourAverageSegments(List<AverageTimeSegment> segments,
                                          LocalDateTime startTime,
                                          LocalDateTime endTime) {
        int startHour = startTime.getHour();
        int endHour = endTime.getHour();
        for (int hour = startHour; hour <= endHour; hour++) {
            LocalDateTime hourStart = startTime.withHour(hour).withMinute(0).withSecond(0).withNano(0);
            LocalDateTime hourEndExclusive = hourStart.plusHours(1);
            LocalDateTime segmentStart = hourStart.isBefore(startTime) ? startTime : hourStart;
            LocalDateTime segmentEndExclusive = hourEndExclusive.isAfter(endTime) ? endTime : hourEndExclusive;
            if (!segmentStart.isBefore(segmentEndExclusive)) {
                continue;
            }
            String label = String.format("%d年%d月%d日%d时",
                    hourStart.getYear(), hourStart.getMonthValue(), hourStart.getDayOfMonth(), hour);
            segments.add(new AverageTimeSegment(label, segmentStart, segmentEndExclusive));
        }
    }

    private void buildDayAverageSegments(List<AverageTimeSegment> segments,
                                         LocalDateTime startTime,
                                         LocalDateTime endTime) {
        LocalDate current = startTime.toLocalDate();
        LocalDate lastDate = resolveLastDateInRange(startTime, endTime);
        while (!current.isAfter(lastDate)) {
            LocalDateTime dayStart = current.atStartOfDay();
            LocalDateTime dayEndExclusive = current.plusDays(1).atStartOfDay();
            LocalDateTime segmentStart = dayStart.isBefore(startTime) ? startTime : dayStart;
            LocalDateTime segmentEndExclusive = dayEndExclusive.isAfter(endTime) ? endTime : dayEndExclusive;
            if (segmentStart.isBefore(segmentEndExclusive)) {
                String label = String.format("%d年%d月%d日", current.getYear(), current.getMonthValue(), current.getDayOfMonth());
                segments.add(new AverageTimeSegment(label, segmentStart, segmentEndExclusive));
            }
            current = current.plusDays(1);
        }
    }

    private void buildMonthAverageSegments(List<AverageTimeSegment> segments,
                                           LocalDateTime startTime,
                                           LocalDateTime endTime) {
        int year = startTime.getYear();
        int month = startTime.getMonthValue();
        int endYear = endTime.getYear();
        int endMonth = endTime.getMonthValue();

        while (year < endYear || (year == endYear && month <= endMonth)) {
            LocalDateTime monthStart = LocalDateTime.of(year, month, 1, 0, 0, 0);
            LocalDateTime monthEndExclusive = monthStart.plusMonths(1);
            LocalDateTime segmentStart = monthStart.isBefore(startTime) ? startTime : monthStart;
            LocalDateTime segmentEndExclusive = monthEndExclusive.isAfter(endTime) ? endTime : monthEndExclusive;
            if (segmentStart.isBefore(segmentEndExclusive)) {
                String label = String.format("%d年%d月", year, month);
                segments.add(new AverageTimeSegment(label, segmentStart, segmentEndExclusive));
            }
            month++;
            if (month > 12) {
                month = 1;
                year++;
            }
        }
    }

    private void buildYearAverageSegments(List<AverageTimeSegment> segments,
                                          LocalDateTime startTime,
                                          LocalDateTime endTime) {
        int year = startTime.getYear();
        int endYear = endTime.getYear();
        while (year <= endYear) {
            LocalDateTime yearStart = LocalDateTime.of(year, 1, 1, 0, 0, 0);
            LocalDateTime yearEndExclusive = yearStart.plusYears(1);
            LocalDateTime segmentStart = yearStart.isBefore(startTime) ? startTime : yearStart;
            LocalDateTime segmentEndExclusive = yearEndExclusive.isAfter(endTime) ? endTime : yearEndExclusive;
            if (segmentStart.isBefore(segmentEndExclusive)) {
                String label = year + "年";
                segments.add(new AverageTimeSegment(label, segmentStart, segmentEndExclusive));
            }
            year++;
        }
    }

    private LocalDate resolveLastDateInRange(LocalDateTime startTime, LocalDateTime endTime) {
        LocalDate endDate = endTime.toLocalDate();
        if (endTime.toLocalTime().equals(LocalTime.MIDNIGHT) && endDate.isAfter(startTime.toLocalDate())) {
            return endDate.minusDays(1);
        }
        return endDate;
    }

    private LocalDateTime parseDateTime(String dateTimeStr) {
        if (!StringUtils.hasText(dateTimeStr)) {
            return null;
        }
        try {
            return LocalDateTime.parse(dateTimeStr.trim(), DATE_TIME_FORMATTER);
        } catch (Exception e) {
            throw new BusinessException("时间格式错误！");
        }
    }

    private static class AverageTimeSegment {
        private final String label;
        private final LocalDateTime startInclusive;
        private final LocalDateTime endExclusive;

        AverageTimeSegment(String label, LocalDateTime startInclusive, LocalDateTime endExclusive) {
            this.label = label;
            this.startInclusive = startInclusive;
            this.endExclusive = endExclusive;
        }

        String getLabel() {
            return label;
        }

        LocalDateTime getStartInclusive() {
            return startInclusive;
        }

        LocalDateTime getEndExclusive() {
            return endExclusive;
        }
    }

    /**
     * 计算所有能源类型的趋势项
     * 当energyTypeId为null时使用，查询项目中所有能源类型设备的总能耗
     */
    private EmsTrendItemVO calculateTrendItemForAllEnergyTypes(Long projectId, String timeDimension, String timePoint) {
        EmsTrendItemVO item = new EmsTrendItemVO();
        item.setTimeLabel(timePoint);

        try {
            String[] timeRange = getTimeRange(timeDimension, timePoint);

            // 对每种能源类型分别统计
            BigDecimal totalAllUsage = BigDecimal.ZERO;
            BigDecimal totalStandardCoal = BigDecimal.ZERO;
            BigDecimal totalCarbonEmission = BigDecimal.ZERO;

            for (Long energyType : Arrays.asList(1L, 2L, 3L)) {
                // 获取该能源类型的设备
                List<String> deviceIds = getDeviceIdsByProject(projectId, energyType);
                if (!deviceIds.isEmpty()) {
                    // 查询该能源类型的用量
                    BigDecimal usage = queryTotalEnergyConsumption(deviceIds, timeRange[0], timeRange[1], energyType);
                    if (usage != null && usage.compareTo(BigDecimal.ZERO) > 0) {
                        totalAllUsage = totalAllUsage.add(usage);

                        // 计算该能源类型的标准煤和碳排放
                        BigDecimal coalFactor = STANDARD_COAL_FACTORS.get(energyType);
                        BigDecimal carbonFactor = CARBON_EMISSION_FACTORS.get(energyType);

                        if (coalFactor != null) {
                            totalStandardCoal = totalStandardCoal.add(usage.multiply(coalFactor));
                        }
                        if (carbonFactor != null) {
                            totalCarbonEmission = totalCarbonEmission.add(usage.multiply(carbonFactor));
                        }
                    }
                }
            }

            // 设置结果
            item.setUsage(totalAllUsage.setScale(2, RoundingMode.HALF_UP));
            item.setStandardCoal(totalStandardCoal.setScale(2, RoundingMode.HALF_UP));
            item.setCarbonEmission(totalCarbonEmission.setScale(2, RoundingMode.HALF_UP));

        } catch (Exception e) {
            System.err.println("Error calculating trend item for all energy types: " + e.getMessage());
            item.setUsage(BigDecimal.ZERO);
            item.setStandardCoal(BigDecimal.ZERO);
            item.setCarbonEmission(BigDecimal.ZERO);
        }

        return item;
    }
}
