package com.xzzn.ems.service.impl;

import com.xzzn.common.enums.DeviceCategory;
import com.xzzn.common.utils.DateUtils;
import com.xzzn.ems.domain.EmsPointConfig;
import com.xzzn.ems.domain.EmsPointMatch;
import com.xzzn.ems.domain.vo.*;
import com.xzzn.ems.mapper.EmsBatteryDataMonthMapper;
import com.xzzn.ems.mapper.EmsPointConfigMapper;
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
import com.xzzn.ems.mapper.EmsPointMatchMapper;
import com.xzzn.ems.service.IGeneralQueryService;
import com.xzzn.ems.service.InfluxPointDataWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 综合查询 服务层实现
 *
 */
@Service
public class GeneralQueryServiceImpl implements IGeneralQueryService
{
    private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:00");

    @Autowired
    private EmsPointMatchMapper emsPointMatchMapper;
    @Autowired
    private EmsPointConfigMapper emsPointConfigMapper;
    @Autowired
    private EmsDevicesSettingMapper emsDevicesSettingMapper;
    @Autowired
    private EmsBatteryDataMonthMapper emsBatteryDataMonthMapper;
    @Autowired
    private InfluxPointDataWriter influxPointDataWriter;

    @Override
    public List<GeneralQueryPointOptionVo> getPointNameList(PointNameRequest request) {
        List<String> siteIds = request.getSiteIds();
        if (siteIds == null || siteIds.isEmpty()) {
            return Collections.emptyList();
        }
        return emsPointConfigMapper.getPointNameList(
                siteIds,
                request.getDeviceCategory(),
                request.getDeviceId(),
                request.getPointName()
        );
    }

    @Override
    public Map<String, List<String>> getAllBatteryIdsBySites(String[] siteIds) {
        Map<String, List<String>> resultMap = new HashMap<>();
        if (siteIds == null || siteIds.length == 0) {
            return resultMap;
        }
        List<SiteBatteryListVo> siteBatteryListVos = emsBatteryDataMonthMapper.getAllBatteryIdsBySites(siteIds);
        resultMap = convertToMap(siteBatteryListVos);
        return resultMap;
    }

    public static Map<String, List<String>> convertToMap(List<SiteBatteryListVo> siteDeviceList) {
        Map<String, List<String>> resultMap = new HashMap<>();

        for (SiteBatteryListVo item : siteDeviceList) {
            String siteId = item.getSiteId ();
            String deviceId = item.getDeviceId ();
            if (!resultMap.containsKey (siteId)) {
                resultMap.put (siteId, new ArrayList<>());
            }
            resultMap.get (siteId).add (deviceId);
        }

        return resultMap;
    }

    @Override
    public List<GeneralQueryResponse> getPointValueList(PointNameRequest request){
        List<String> siteIds = request.getSiteIds();
        if (siteIds == null || siteIds.isEmpty()) {
            return Collections.emptyList();
        }

        String deviceCategory = request.getDeviceCategory();
        List<String> pointIds = resolvePointIds(request);

        List<String> pointNames = resolvePointNames(request);
        if (pointIds.isEmpty() && pointNames.isEmpty()) {
            return Collections.emptyList();
        }

        // 处理时间范围，如果未传根据数据单位设默认值
        dealDataTime(request);
        Date startDate = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, request.getStartDate());
        Date endDate = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, request.getEndDate());
        if (request.getDataUnit() == 3) {
            endDate = DateUtils.adjustToEndOfDay(request.getEndDate());
        }

        List<String> selectedDeviceIds = pointIds.isEmpty() ? resolveSelectedDeviceIds(request) : Collections.emptyList();
        List<EmsPointConfig> pointConfigs = emsPointConfigMapper.getConfigListForGeneralQuery(
                siteIds, deviceCategory, pointIds, pointNames, selectedDeviceIds
        );
        if (pointConfigs == null || pointConfigs.isEmpty()) {
            return Collections.emptyList();
        }
        Map<String, String> selectedPointNameById = buildSelectedPointNameById(request);

        List<GeneralQueryDataVo> dataVoList = new ArrayList<>();
        for (EmsPointConfig pointConfig : pointConfigs) {
            String selectedPointName = selectedPointNameById.get(resolveInfluxPointKey(pointConfig));
            dataVoList.addAll(queryPointCurve(pointConfig, request.getDataUnit(), startDate, endDate, selectedPointName));
        }

        if (dataVoList.isEmpty()) {
            return Collections.emptyList();
        }

        if (request.getDataUnit() == 1) {
            try {
                dataVoList = dealWithMinutesData(siteIds, dataVoList, deviceCategory, request.getStartDate(), request.getEndDate());
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
        return convertCommonToResultList(dataVoList, 1L);
    }

    private List<String> resolvePointNames(PointNameRequest request) {
        List<String> names = new ArrayList<>();
        if (request.getPointNames() != null && !request.getPointNames().isEmpty()) {
            names.addAll(request.getPointNames());
        } else if (request.getPointName() != null && !"".equals(request.getPointName().trim())) {
            names.addAll(Arrays.stream(request.getPointName().split(","))
                    .map(String::trim)
                    .filter(s -> !s.isEmpty())
                    .collect(Collectors.toList()));
        }
        return names.stream().distinct().collect(Collectors.toList());
    }

    private List<String> resolvePointIds(PointNameRequest request) {
        List<String> ids = new ArrayList<>();
        if (request.getPointIds() != null && !request.getPointIds().isEmpty()) {
            ids.addAll(request.getPointIds());
        } else if (request.getPointId() != null && !"".equals(request.getPointId().trim())) {
            ids.addAll(Arrays.stream(request.getPointId().split(","))
                    .map(String::trim)
                    .filter(s -> !s.isEmpty())
                    .collect(Collectors.toList()));
        }
        return ids.stream().distinct().collect(Collectors.toList());
    }

    private List<String> resolveSelectedDeviceIds(PointNameRequest request) {
        List<String> selected = new ArrayList<>();
        if (request.getDeviceId() != null && !"".equals(request.getDeviceId().trim())) {
            selected.add(request.getDeviceId().trim());
        }
        Map<String, List<String>> siteDeviceMap = request.getSiteDeviceMap();
        if (siteDeviceMap != null && !siteDeviceMap.isEmpty()) {
            for (List<String> devices : siteDeviceMap.values()) {
                if (devices == null) {
                    continue;
                }
                for (String deviceId : devices) {
                    if (deviceId != null && !"".equals(deviceId.trim())) {
                        selected.add(deviceId.trim());
                    }
                }
            }
        }
        return selected.stream().distinct().collect(Collectors.toList());
    }

    private List<GeneralQueryDataVo> queryPointCurve(EmsPointConfig config, int dataUnit, Date startDate, Date endDate,
                                                     String selectedPointName) {
        if (config == null || config.getSiteId() == null) {
            return Collections.emptyList();
        }
        String influxPointKey = resolveInfluxPointKey(config);
        if (influxPointKey == null) {
            return Collections.emptyList();
        }
        // 与点位列表曲线保持一致：按 siteId + pointKey 查询，避免 deviceId 维度导致综合查询漏数
        List<InfluxPointDataWriter.PointValue> values = influxPointDataWriter.queryCurveDataByPointKey(
                config.getSiteId(), influxPointKey, startDate, endDate
        );
        if (values == null || values.isEmpty()) {
            return Collections.emptyList();
        }

        // 每个时间桶取该桶最后一条值
        Map<String, Object> latestByBucket = new LinkedHashMap<>();
        for (InfluxPointDataWriter.PointValue value : values) {
            latestByBucket.put(formatByDataUnit(value.getDataTime(), dataUnit), value.getPointValue());
        }

        List<GeneralQueryDataVo> result = new ArrayList<>();
        String displayDeviceId = buildDisplayDeviceId(config, selectedPointName);
        for (Map.Entry<String, Object> entry : latestByBucket.entrySet()) {
            GeneralQueryDataVo vo = new GeneralQueryDataVo();
            vo.setSiteId(config.getSiteId());
            vo.setDeviceId(displayDeviceId);
            vo.setValueDate(entry.getKey());
            vo.setPointValue(entry.getValue());
            result.add(vo);
        }
        return result;
    }

    private String resolveInfluxPointKey(EmsPointConfig config) {
        if (config == null) {
            return null;
        }
        if (config.getPointId() != null && !"".equals(config.getPointId().trim())) {
            return config.getPointId().trim();
        }
        return null;
    }

    private String buildDisplayDeviceId(EmsPointConfig config, String selectedPointName) {
        if (selectedPointName != null && !"".equals(selectedPointName.trim())) {
            return selectedPointName.trim();
        }
        String pointName = config.getPointName() == null || "".equals(config.getPointName().trim())
                ? config.getDataKey() : config.getPointName().trim();
        return pointName;
    }

    private Map<String, String> buildSelectedPointNameById(PointNameRequest request) {
        Map<String, String> selectedNameById = new HashMap<>();
        if (request == null || request.getPointIds() == null || request.getPointNames() == null) {
            return selectedNameById;
        }
        List<String> pointIds = request.getPointIds();
        List<String> pointNames = request.getPointNames();
        int size = Math.min(pointIds.size(), pointNames.size());
        for (int i = 0; i < size; i++) {
            String pointId = pointIds.get(i);
            String pointName = pointNames.get(i);
            if (pointId == null || "".equals(pointId.trim()) || pointName == null || "".equals(pointName.trim())) {
                continue;
            }
            selectedNameById.put(pointId.trim(), pointName.trim());
        }
        return selectedNameById;
    }

    private String formatByDataUnit(Date dataTime, int dataUnit) {
        if (dataTime == null) {
            return "";
        }
        if (dataUnit == 3) {
            return new SimpleDateFormat("yyyy-MM-dd").format(dataTime);
        }
        if (dataUnit == 2) {
            return new SimpleDateFormat("yyyy-MM-dd HH:00").format(dataTime);
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:00").format(dataTime);
    }

    private List<GeneralQueryResponse> generalQueryCommonData(List<String> querySiteIds, String tableName,
                                                              String tableField, PointNameRequest request,
                                                              String deviceCategory, Long dataType) throws ParseException {
        List<GeneralQueryResponse> result = new ArrayList<>();
        List<GeneralQueryDataVo> dataVoList = new ArrayList<>();

        int dataUnit = request.getDataUnit();
        String deviceId = request.getDeviceId();
        Date startDate  = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS,request.getStartDate());
        Date endDate  = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS,request.getEndDate());
        if (dataUnit == 1) { // 分钟：yyyy-MM-dd HH:mm:ss
            startDate = DateUtils.adjustToStartOfMinutes(request.getStartDate());
            dataVoList = emsPointMatchMapper.getCommonPointDataByMinutes(querySiteIds,tableName,tableField,startDate,endDate,deviceId);
            if (dataVoList != null && dataVoList.size() > 0) {
                dataVoList = dealWithMinutesData(querySiteIds,dataVoList,deviceCategory,
                        request.getStartDate(),request.getEndDate());
            }
        } else if (dataUnit == 2) { // 小时：yyyy-MM-dd HH:mm:ss
            dataVoList = emsPointMatchMapper.getCommonPointDataByHours(querySiteIds,tableName,tableField,startDate,endDate,deviceId);
        } else if (dataUnit == 3) { // 天：yyyy-MM-dd 00:00:00
            endDate = DateUtils.adjustToEndOfDay(request.getEndDate());
            dataVoList = emsPointMatchMapper.getCommonPointDataForBoxPlot(querySiteIds,tableName,tableField,startDate,endDate,deviceId);
        }

        if (dataUnit == 3) {
            // 箱线图数据特殊处理
            result = dealCommonWithBoxPlotData(dataVoList, dataType);
        } else {
            // 曲线图数据特殊处理
            // 数据转换+计算曲线数据最大最小平均和差值
            result =  convertCommonToResultList(dataVoList, dataType);
        }

        return  result;
    }

    private List<GeneralQueryResponse> generalQueryBatteryData(List<String> querySiteIds, String tableName,
                                                               String tableField, PointNameRequest request,
                                                               String deviceCategory, Long dataType) throws ParseException {
        List<GeneralQueryResponse> result = new ArrayList<>();
        List<GeneralQueryDataVo> dataVoList = new ArrayList<>();

        int dataUnit = request.getDataUnit();
        String clusterDeviceId = request.getDeviceId();
        Date startDate   = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS,request.getStartDate());
        Date endDate   = DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS,request.getEndDate());
        Map<String,List<String>> siteDeviceMap = request.getSiteDeviceMap();
        if (dataUnit == 1) { // 分钟
            startDate = DateUtils.adjustToStartOfMinutes(request.getStartDate());
            dataVoList = emsPointMatchMapper.getBatteryPointDataByMinutes(querySiteIds,tableName,tableField,startDate,endDate,siteDeviceMap,clusterDeviceId);
            if (dataVoList != null && dataVoList.size() > 0) {
                dataVoList = dealWithBatteryMinutesData(querySiteIds,dataVoList,deviceCategory,
                        request.getStartDate(),request.getEndDate(),siteDeviceMap);
            }
        } else if (dataUnit == 2) { // 小时
            startDate = DateUtils.adjustToStartOfHour(request.getStartDate());
            tableName = "ems_battery_data_hour";
            dataVoList = emsPointMatchMapper.getBatteryPointDataByHours(querySiteIds,tableName,tableField,startDate,endDate,siteDeviceMap,clusterDeviceId);
        } else if (dataUnit == 3) { // 天
            endDate = DateUtils.adjustToEndOfDay(request.getEndDate());
            tableName = "ems_battery_data_day";
            dataVoList = emsPointMatchMapper.getBatteryPointDataForBoxPlot(querySiteIds,tableName,tableField,startDate,endDate,siteDeviceMap,clusterDeviceId);
        }
        // 数据转换
        if (dataUnit == 3) {
            // 箱线图数据特殊处理
            result = dealBatteryWithBoxPlotData(dataVoList, dataType);
        } else {
            // 曲线图数据特殊处理
            // 数据转换+计算曲线数据最大最小平均和差值
            result =  convertBatteryToResultList(dataVoList, dataType);
        }
        return  result;
    }

    private List<GeneralQueryResponse> dealCommonWithBoxPlotData(List<GeneralQueryDataVo> dataVoList, Long dataType) {
        // 数据转换: 先按siteId分组，再按deviceId分组
        return dataVoList.stream()
                // 第一层分组：按siteId分组，得到 <siteId, 该站点所有数据>
                .collect(Collectors.groupingBy(GeneralQueryDataVo::getSiteId))
                .entrySet().stream()
                .map(siteEntry -> {
                    String siteId = siteEntry.getKey();
                    List<GeneralQueryDataVo> siteAllData = siteEntry.getValue();

                    // 第二层分组：在当前站点下，按deviceId分组，得到 <deviceId, 该设备所有数据>
                    List<DevicePointDataList> deviceList = siteAllData.stream()
                            .collect(Collectors.groupingBy(GeneralQueryDataVo::getDeviceId))
                            .entrySet().stream()
                            .map(deviceEntry -> {
                                String deviceId = deviceEntry.getKey();
                                List<GeneralQueryDataVo> deviceDataList = deviceEntry.getValue();

                                // 第三层分组：在当前设备下，按时间分组，得到 <时间, 该时间段数据>
                               List<GeneralQueryDataVo> dataList = deviceDataList.stream().sorted(Comparator.comparing(GeneralQueryDataVo::getValueDate))
                                        .collect(Collectors.groupingBy(
                                                GeneralQueryDataVo::getValueDate,
                                                TreeMap::new,
                                                Collectors.toList()))
                                        .entrySet().stream().map(timeEntry -> {
                                            String valueDate = timeEntry.getKey();
                                            // 计算当前设备数据的统计值（最小值，第一四分位数，中位数，第三四分位数, 最大值）
                                            List<BigDecimal> values = timeEntry.getValue().stream().map(data -> new BigDecimal(String.valueOf(data.getPointValue()))).collect(Collectors.toList());
                                            BoxPlotData data = calculateBoxPlotData(values);
                                            return new GeneralQueryDataVo(siteId, deviceId, valueDate, null, data.min, data.q1, data.median, data.q3, data.max);
                                        }).collect(Collectors.toList());

                                return new DevicePointDataList(deviceId, null, dataList);
                            })
                            .collect(Collectors.toList());

                    return new GeneralQueryResponse(siteId, deviceList, dataType, 2L);
                })
                .collect(Collectors.toList());
    }

    private List<GeneralQueryResponse> dealBatteryWithBoxPlotData(List<GeneralQueryDataVo> dataVoList, Long dataType) {
        // 先按siteId分组
        return dataVoList.stream()
                .collect(Collectors.groupingBy(GeneralQueryDataVo::getSiteId))
                .entrySet().stream()
                .map(siteEntry -> {
                    String siteId = siteEntry.getKey();
                    List<GeneralQueryDataVo> siteData = siteEntry.getValue();

                    // 2. 按(deviceId + parentDeviceId)组合分组，生成deviceList
                    List<DevicePointDataList> deviceList = siteData.stream()
                            // 分组键：deviceId + parentDeviceId（确保唯一设备）
                            .collect(Collectors.groupingBy(data ->
                                    data.getDeviceId() + "_" + data.getParentDeviceId()
                            ))
                            .entrySet().stream()
                            .map(deviceEntry -> {
                                // 解析分组键，获取deviceId和parentDeviceId
                                String[] keyParts = deviceEntry.getKey().split("_");
                                String deviceId = keyParts[0];
                                String parentDeviceId = keyParts[1];

                                // 3. 第三层分组：在当前设备下，按时间分组，得到 <时间, 该时间段数据>
                                List<GeneralQueryDataVo> deviceDataList = deviceEntry.getValue().stream()
                                        .collect(Collectors.groupingBy(
                                                GeneralQueryDataVo::getValueDate,
                                                TreeMap::new,
                                                Collectors.toList()))
                                        .entrySet().stream().map(timeEntry -> {
                                            String valueDate = timeEntry.getKey();

                                            // 计算当前设备数据的统计值（最小值，第一四分位数，中位数，第三四分位数, 最大值）
                                            List<BigDecimal> values = timeEntry.getValue().stream().map(data -> new BigDecimal(String.valueOf(data.getPointValue()))).collect(Collectors.toList());
                                            BoxPlotData data = calculateBoxPlotData(values);
                                            return new GeneralQueryDataVo(siteId, deviceId, valueDate, parentDeviceId, data.min, data.q1, data.median, data.q3, data.max);
                                        }).collect(Collectors.toList());

                                // 4. 构建DeviceItem
                                return new DevicePointDataList(deviceId, parentDeviceId, deviceDataList);
                            })// 关键排序步骤：先按deviceId升序，再按parentDeviceId升序
                            .sorted(
                                    Comparator.comparing(DevicePointDataList::getDeviceId)  // 第一排序键：deviceId
                                            .thenComparing(DevicePointDataList::getParentDeviceId)  // 第二排序键：parentDeviceId
                            )
                            .collect(Collectors.toList());

                    // 5. 构建SiteData
                    return new GeneralQueryResponse(siteId, deviceList, dataType, 2L);
                })
                .collect(Collectors.toList());
    }

    private List<GeneralQueryResponse> convertBatteryToResultList(List<GeneralQueryDataVo> dataVoList, Long dataType) {
        // 先按siteId分组
        return dataVoList.stream()
                .collect(Collectors.groupingBy(GeneralQueryDataVo::getSiteId))
                .entrySet().stream()
                .map(siteEntry -> {
                    String siteId = siteEntry.getKey();
                    List<GeneralQueryDataVo> siteData = siteEntry.getValue();

                    // 2. 按(deviceId + parentDeviceId)组合分组，生成deviceList
                    List<DevicePointDataList> deviceList = siteData.stream()
                            // 分组键：deviceId + parentDeviceId（确保唯一设备）
                            .collect(Collectors.groupingBy(data ->
                                    data.getDeviceId() + "_" + data.getParentDeviceId()
                            ))
                            .entrySet().stream()
                            .map(deviceEntry -> {
                                // 解析分组键，获取deviceId和parentDeviceId
                                String[] keyParts = deviceEntry.getKey().split("_");
                                String deviceId = keyParts[0];
                                String parentDeviceId = keyParts[1];

                                // 3. 转换为PointValue列表
                                List<GeneralQueryDataVo> pointValueList = deviceEntry.getValue().stream()
                                        .map(data -> {
                                            GeneralQueryDataVo pv = new GeneralQueryDataVo();
                                            pv.setSiteId(siteId);
                                            pv.setDeviceId(deviceId);
                                            pv.setValueDate(data.getValueDate());
                                            pv.setPointValue(data.getPointValue());
                                            pv.setParentDeviceId(parentDeviceId);
                                            return pv;
                                        })
                                        .collect(Collectors.toList());

                                // 4. 计算最大最小平均值
                                Stats stats = clacStats(pointValueList);

                                // 4. 构建DeviceItem
                                return new DevicePointDataList(deviceId, pointValueList,parentDeviceId,
                                        stats.max, stats.min,stats.avg,stats.diff,stats.maxDate,stats.minDate,
                                        stats.q1, stats.median, stats.q3);
                            })// 关键排序步骤：先按deviceId升序，再按parentDeviceId升序
                            .sorted(
                                    Comparator.comparing(DevicePointDataList::getDeviceId)  // 第一排序键：deviceId
                                            .thenComparing(DevicePointDataList::getParentDeviceId)  // 第二排序键：parentDeviceId
                            )
                            .collect(Collectors.toList());

                    // 5. 构建SiteData
                    return new GeneralQueryResponse(siteId, deviceList, dataType, 1L);
                })
                .collect(Collectors.toList());
    }

    private List<GeneralQueryResponse> convertCommonToResultList(List<GeneralQueryDataVo> dataVoList, Long dataType) {
        // 数据转换: 先按siteId分组，再按deviceId分组
         return dataVoList.stream()
                // 第一层分组：按siteId分组，得到 <siteId, 该站点所有数据>
                .collect(Collectors.groupingBy(GeneralQueryDataVo::getSiteId))
                .entrySet().stream()
                .map(siteEntry -> {
                    String siteId = siteEntry.getKey();
                    List<GeneralQueryDataVo> siteAllData = siteEntry.getValue();

                    // 第二层分组：在当前站点下，按deviceId分组，得到 <deviceId, 该设备所有数据>
                    List<DevicePointDataList> deviceList = siteAllData.stream()
                            .collect(Collectors.groupingBy(GeneralQueryDataVo::getDeviceId))
                            .entrySet().stream()
                            .map(deviceEntry -> {
                                String deviceId = deviceEntry.getKey();
                                List<GeneralQueryDataVo> deviceDataList = deviceEntry.getValue();

                                // 计算当前设备数据的统计值（最大，最小，平均，差值）
                                Stats stats = clacStats(deviceDataList);

                                return  new DevicePointDataList(deviceId, deviceDataList,null,
                                                    stats.max, stats.min,stats.avg,stats.diff,stats.maxDate,stats.minDate,
                                                    stats.q1, stats.median, stats.q3);
                            })
                            .collect(Collectors.toList());

                    return new GeneralQueryResponse(siteId, deviceList, dataType, 1L);
                })
                .collect(Collectors.toList());

    }

    private Stats clacStats(List<GeneralQueryDataVo> deviceDataList) {
        // 转换并过滤数据，封装数值和对应的时间
        List<ValueTimePair> validPairs = deviceDataList.stream()
                .map(vo -> {
                    BigDecimal value = convertToBigDecimal(vo.getPointValue());
                    return value != null ?
                            new ValueTimePair(value, vo.getValueDate()) :
                            null;
                })
                .filter(pair -> pair != null)  // 过滤无效数据
                .collect(Collectors.toList());

        if (validPairs.isEmpty()) {
            return new Stats(null, null, null, null, null, null, null, null, null);
        }
        // 计算最大最小值
        Optional<ValueTimePair> maxPair = validPairs.stream().max((p1, p2) -> p1.value.compareTo(p2.value));
        Optional<ValueTimePair> minPair = validPairs.stream().min((p1, p2) -> p1.value.compareTo(p2.value));
        // 计算平均值,四舍五入保留4为小数
        BigDecimal sum = validPairs.stream() .map(pair -> pair.value).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal avgValue = sum.divide(BigDecimal.valueOf(validPairs.size()), 4, BigDecimal.ROUND_HALF_UP);
        // 增量数据，计算差值
        BigDecimal diff = maxPair.get().value.subtract(minPair.get().value);
        // 计算分位数统计值
        List<BigDecimal> sortedValues = validPairs.stream()
                .map(pair -> pair.value)
                .sorted()
                .collect(Collectors.toList());
        BoxPlotData boxStats = calculateBoxPlotData(sortedValues);

        return  new Stats(maxPair.get().value,minPair.get().value,avgValue,diff,maxPair.get().time,minPair.get().time,
                boxStats == null ? null : boxStats.q1,
                boxStats == null ? null : boxStats.median,
                boxStats == null ? null : boxStats.q3);
    }

    private BigDecimal convertToBigDecimal(Object pointValue) {
        if (pointValue == null) {
            return null;
        }
        // 根据实际存储的数值类型，依次判断并转换
        if (pointValue instanceof BigDecimal) {
            return (BigDecimal) pointValue;
        } else if (pointValue instanceof Integer) {
            return new BigDecimal ((Integer) pointValue);
        } else if (pointValue instanceof Long) {
            return new BigDecimal ((Long) pointValue);
        } else if (pointValue instanceof Double) {
            return BigDecimal.valueOf ((Double) pointValue); // 避免 Double 精度丢失
        } else if (pointValue instanceof Float) {
            return BigDecimal.valueOf ((Float) pointValue);
        } else {
            // 非数值类型（如 String），可根据业务需求处理（此处返回 null）
            return null;
        }
    }

    private void dealDataTime(PointNameRequest request) {
        String startDate   = request.getStartDate();
        String endDate   = request.getEndDate();
        int dataUnit = request.getDataUnit();
        if (startDate == null || endDate == null) {
            // 前端未传递时，根据dataunit设置默认时间范围
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime start;
            LocalDateTime end;
            switch (dataUnit) {
                case 1: // 按分钟 - 前一小时
                    end = now;
                    start = now.minusHours(1);
                    break;
                case 2: // 按小时 - 当天
                    end = now.with(LocalTime.MAX);
                    start = now.with(LocalTime.MIN);
                    break;
                case 3: // 按天 - 当月
                    end = now.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
                    start = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
                    break;
                default:
                    throw new IllegalArgumentException("无效的dataunit值: " + dataUnit);
            }
            // 格式化为字符串（与前端传递的格式一致）
            startDate = DateUtils.convertToString(start);
            endDate = DateUtils.convertToString(end);
        }
        request.setStartDate(startDate);
        request.setEndDate(endDate);
    }

    private List<GeneralQueryDataVo> dealWithMinutesData(List<String> querySiteIds, List<GeneralQueryDataVo> dataVoList,
                                                         String deviceCategory, String startDate, String endDate) throws ParseException {
        List<GeneralQueryDataVo> fullData = new ArrayList<>();
        Map<String, GeneralQueryDataVo> dataMap = new HashMap<>();
        List<String> deviceIds =  new ArrayList<>();
        for (GeneralQueryDataVo data : dataVoList) {
            String key = data.getSiteId() + "_" + data.getDeviceId() + "_" + data.getValueDate();
            dataMap.put(key, data);

            if (!deviceIds.contains(data.getDeviceId())) {
                deviceIds.add(data.getDeviceId());
            }
        }
        // 生成分钟序列
        Set<String> minuteSeries = generateMinuteSeries(startDate, endDate);
        // 遍历每个站点,查看设备
        for (String siteId : querySiteIds) {
            // 处理该站点下的所有设备
            for (String deviceId : deviceIds) {
                Object lastValue = null;

                for (String minute : minuteSeries) {
                    String dataKey = siteId + "_" + deviceId + "_" + minute;
                    GeneralQueryDataVo data = dataMap.get(dataKey);

                    // 空值填充逻辑
                    if (data == null) {
                        data = new GeneralQueryDataVo();
                        data.setValueDate(minute);
                        data.setSiteId(siteId);
                        data.setDeviceId(deviceId);
                        data.setPointValue(lastValue==null? BigDecimal.ZERO : lastValue); // 用上一分钟值填充
                    } else {
                        lastValue = data.getPointValue(); // 更新最新值
                    }

                    fullData.add(data);
                }
            }
        }

        fullData.sort(Comparator.comparing(GeneralQueryDataVo::getValueDate)
                .thenComparing(GeneralQueryDataVo::getSiteId)
                .thenComparing(GeneralQueryDataVo::getDeviceId));

        return fullData;
    }

    private List<GeneralQueryDataVo> dealWithBatteryMinutesData(List<String> querySiteIds, List<GeneralQueryDataVo> dataVoList,
                                                                String deviceCategory, String startDate, String endDate,
                                                                Map<String, List<String>> siteDeviceMap) throws ParseException {
        List<GeneralQueryDataVo> fullData = new ArrayList<>();
        Map<String, GeneralQueryDataVo> dataMap = new HashMap<>();
        List<String> clusterIds = new ArrayList<>();
        for (GeneralQueryDataVo data : dataVoList) {
            String key = data.getParentDeviceId() + "_" + data.getDeviceId() + "_" + data.getValueDate();
            dataMap.put(key, data);

            if (!clusterIds.contains(data.getParentDeviceId())) {
                clusterIds.add(data.getParentDeviceId());
            }
        }
        // 生成分钟序列
        Set<String> minuteSeries = generateMinuteSeries(startDate, endDate);

        Map<String, Object> lastValueMap = new HashMap<>();
        String siteId = querySiteIds.get(0);
        // 单站-先处理站点下的簇id
        for (String clusterId : clusterIds) {
            List<String> deviceIds = siteDeviceMap.get(siteId);
            if (deviceIds.isEmpty()) {
                continue;
            }
            for (String deviceId : deviceIds) {
                String deviceKey = clusterId + "_" + deviceId;

                for (String minute : minuteSeries) {
                    Object lastValue = lastValueMap.get(deviceKey);
                    String dataKey = clusterId + "_" + deviceId + "_" + minute;
                    GeneralQueryDataVo data = dataMap.get(dataKey);

                    // 空值填充逻辑
                    if (data == null) {
                        data = new GeneralQueryDataVo();
                        data.setValueDate(minute);
                        data.setSiteId(siteId);
                        data.setDeviceId(deviceId);
                        data.setParentDeviceId(clusterId);
                        data.setPointValue(lastValue == null ? 0 : lastValue); // 上一分钟数据不存在默认0
                    } else {
                        lastValueMap.put(deviceKey,data.getPointValue());// 更新最新值
                    }

                    fullData.add(data);
                }
            }

        }

        fullData.sort(Comparator.comparing(GeneralQueryDataVo::getValueDate)
                .thenComparing(GeneralQueryDataVo::getSiteId)
                .thenComparing(GeneralQueryDataVo::getDeviceId));

        return fullData;
    }

    /**
     * 根据前端传递的startDate和endDate生成所有分钟点的序列
     * @param startDate 起始时间（格式：yyyy-MM-dd HH:mm:00）
     * @param endDate 结束时间（格式：yyyy-MM-dd HH:mm:00）
     * @return 包含所有分钟点的有序集合
     * @throws ParseException 时间格式解析失败时抛出
     */
    private Set<String> generateMinuteSeries(String startDate, String endDate) throws ParseException {
        Set<String> minutes = new TreeSet<>(); // TreeSet保证时间有序

        // 解析起止时间
        //startDate = DateUtils.adjustToNextMinute(startDate);
        Date startTime = TIME_FORMAT.parse(startDate.substring(0, 16) + ":00");
        Date endTime = TIME_FORMAT.parse(endDate.substring(0, 16) + ":00");

        // 验证时间有效性
        if (startTime.after(endTime)) {
            throw new IllegalArgumentException("起始时间不能晚于结束时间");
        }

        // 初始化日历对象，从起始时间开始
        Calendar cal = Calendar.getInstance();
        cal.setTime(startTime);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        // 循环生成从startDate到endDate的所有分钟点
        while (!cal.getTime().after(endTime)) {
            // 添加当前分钟（格式：yyyy-MM-dd HH:mm:00）
            minutes.add(TIME_FORMAT.format(cal.getTime()));
            // 递增1分钟
            cal.add(Calendar.MINUTE, 1);
        }

        return minutes;
    }

    public BoxPlotData calculateBoxPlotData(List<BigDecimal> values) {
        if (values == null || values.isEmpty()) {
            return null;
        }

        // 排序
        Collections.sort(values);

        // 计算五个统计量
        BigDecimal min = values.get(0);
        BigDecimal max = values.get(values.size() - 1);
        BigDecimal median = calculateMedian(values);
        BigDecimal q1 = calculateQuartile(values, new BigDecimal("0.25"));
        BigDecimal q3 = calculateQuartile(values, new BigDecimal("0.75"));

        return new BoxPlotData(min, q1, median, q3, max);
    }

    private BigDecimal calculateMedian(List<BigDecimal> values) {
        int size = values.size();
        if (size % 2 == 0) {
            return (values.get(size/2 - 1).add(values.get(size/2))).divide(new BigDecimal(2));
        } else {
            return values.get(size/2);
        }
    }

    private BigDecimal calculateQuartile(List<BigDecimal> values, BigDecimal quartile) {
        int size = values.size();
        BigDecimal pos = new BigDecimal(size - 1).multiply(quartile);
        int lowerIndex = (int) Math.floor(pos.doubleValue());
        int upperIndex = (int) Math.ceil(pos.doubleValue());

        if (lowerIndex == upperIndex) {
            return values.get(lowerIndex);
        }

        BigDecimal lowerValue = values.get(lowerIndex);
        BigDecimal upperValue = values.get(upperIndex);
//        return lowerValue + (upperValue - lowerValue) * (pos - lowerIndex);
        return lowerValue.add(upperValue.subtract(lowerValue)).multiply((pos.subtract(new BigDecimal(lowerIndex))));
    }

    /**
     * 内部辅助类：存储统计结果
     */
    private static class Stats {
        private final BigDecimal max;
        private final String maxDate;
        private final BigDecimal min;
        private final String minDate;
        private final BigDecimal avg;
        private final BigDecimal diff;
        private final BigDecimal q1;
        private final BigDecimal median;
        private final BigDecimal q3;

        public Stats(BigDecimal max, BigDecimal min, BigDecimal avg, BigDecimal diff,
                     String maxDate, String minDate,
                     BigDecimal q1, BigDecimal median, BigDecimal q3) {
            this.max = max;
            this.maxDate = maxDate;
            this.min = min;
            this.minDate = minDate;
            this.avg = avg;
            this.diff = diff;
            this.q1 = q1;
            this.median = median;
            this.q3 = q3;
        }
    }

    /**
     * 辅助类：关联数值和对应的时间
     */
    private static class ValueTimePair {
        final BigDecimal value;
        final String time;

        ValueTimePair(BigDecimal value, String time) {
            this.value = value;
            this.time = time;
        }
    }

    /**
     * 内部辅助类：存储箱线图统计结果
     */
    private static class BoxPlotData {
        /** 最小值(Minimum) */
        private final BigDecimal min;

        /** 第一四分位数(Q1) */
        private final BigDecimal q1;

        /** 中位数(Median/Q2) */
        private final BigDecimal median;

        /** 第三四分位数(Q3) */
        private final BigDecimal q3;

        /** 最大值(Maximum) */
        private final BigDecimal max;

        public BoxPlotData(BigDecimal min, BigDecimal q1, BigDecimal median, BigDecimal q3, BigDecimal max) {
            this.min = min;
            this.q1 = q1;
            this.median = median;
            this.q3 = q3;
            this.max = max;
        }
    }
}
