package cn.iocoder.yudao.module.mes.service.energydevice;

import cn.iocoder.yudao.framework.common.pojo.DeviceEdgeData;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.JavaToTdengineTypeEnum;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.HourEnergyValueVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper;
import cn.iocoder.yudao.module.iot.service.device.TDengineService;
import cn.iocoder.yudao.module.mes.controller.admin.energydevice.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.energydevice.EnergyDeviceCheckRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.energydevice.EnergyDeviceDO;
import cn.iocoder.yudao.module.mes.dal.mysql.energydevice.EnergyDeviceCheckRecordMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.energydevice.EnergyDeviceMapper;
import cn.iocoder.yudao.module.mes.dal.redis.no.MesNoRedisDAO;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_ID_DOES_NOT_EXIST;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.POINT_ID_MODEL_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;

/**
 * 能源设备 Service 实现类
 *
 * @author 内蒙必硕
 */
@Service
@Validated
@Slf4j
public class EnergyDeviceServiceImpl implements EnergyDeviceService {

    @Resource
    private EnergyDeviceMapper energyDeviceMapper;
    @Resource
    private EnergyDeviceCheckRecordMapper energyDeviceCheckRecordMapper;
    @Resource
    private MesNoRedisDAO noRedisDAO;
    @Resource
    private TDengineService tDengineService;
    @Resource
    private DeviceContactModelMapper deviceContactModelMapper;

    @Override
    public Long createEnergyDevice(EnergyDeviceSaveReqVO createReqVO) {
        // 插入
        EnergyDeviceDO energyDevice = BeanUtils.toBean(createReqVO, EnergyDeviceDO.class);
        if (StringUtils.isEmpty(energyDevice.getCode())) {
            // 1.4 生成编号
            String no = noRedisDAO.generate2(MesNoRedisDAO.ENERGY_NO_PREFIX);
            energyDevice.setCode(no);
        }
        // 校验编码是否唯一
        if (StringUtils.isNotEmpty(energyDevice.getCode())) {
            List<EnergyDeviceDO> existingDevice = energyDeviceMapper.selectList(
                    Wrappers.<EnergyDeviceDO>lambdaQuery().eq(EnergyDeviceDO::getCode,createReqVO.getCode()));
            if (!existingDevice.isEmpty()) {
                throw exception(ENERGY_DEVICE_CODE_DUPLICATE);
            }
        }
        //校验运算规则
        verifiyOperationRules(createReqVO.getOperationRulesVOList());
        energyDevice.setRules(JSON.toJSONString(createReqVO.getOperationRulesVOList()));
        energyDeviceMapper.insert(energyDevice);
        // 返回
        return energyDevice.getId();
    }

    /**
     * 校验运算规则列表
     *
     * 规则：
     * 1. 列表不能为空
     * 2. 每个元素的设备ID和点位ID不能为空
     * 3. 运算符只能是 +、-、*、/ 四种
     * 4. 最后一个元素的运算符必须为空
     * 5. 除最后一个元素外，其他元素必须都有运算符
     * 6. 系数不能为null，除法时除数不能为0（在计算时校验）
     * 7. 不能连续出现两个运算符
     */
    private void verifiyOperationRules(List<OperationRulesVO> operationRulesVOList) {
        if (CollectionUtils.isEmpty(operationRulesVOList)) {
            throw exception(CALCULATION_RULE_NOT_EXISTS);
        }

        // 运算符集合
        Set<String> validOperators = new HashSet<>(Arrays.asList("+", "-", "*", "/"));

        for (int i = 0; i < operationRulesVOList.size(); i++) {
            OperationRulesVO rule = operationRulesVOList.get(i);

            // 1. 校验设备ID和点位ID
            if (rule.getDeviceId() == null) {
                throw exception(DEVICE_ID_DOES_NOT_EXIST);
            }
            if (rule.getPointId() == null) {
                throw exception(POINT_ID_MODEL_NOT_EXISTS);
            }

            // 2. 获取运算符
            String operator = rule.getOperator();
            boolean isLastElement = (i == operationRulesVOList.size() - 1);

            if (isLastElement) {
                // 最后一个元素：运算符必须为空
                if (StringUtils.isNotBlank(operator)) {
                    throw exception(CALCULATION_LAST_RULE_NO_OPERATOR);
                }
            } else {
                // 非最后一个元素：必须有运算符
                if (StringUtils.isBlank(operator)) {
                    throw exception(CALCULATION_OPERATOR_MISSING);
                }

                // 校验运算符是否合法
                if (!validOperators.contains(operator)) {
                    throw exception(CALCULATION_OPERATOR_INVALID);
                }

            }
        }
    }

    @Override
    public void updateEnergyDevice(EnergyDeviceSaveReqVO updateReqVO) {
        // 校验存在
        validateEnergyDeviceExists(updateReqVO.getId());
        // 校验运算规则
        verifiyOperationRules(updateReqVO.getOperationRulesVOList());

        // 更新
        EnergyDeviceDO updateObj = BeanUtils.toBean(updateReqVO, EnergyDeviceDO.class);
        updateObj.setRules(JSON.toJSONString(updateReqVO.getOperationRulesVOList()));

        energyDeviceMapper.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteEnergyDevice(Long id) {
        // 校验存在
        validateEnergyDeviceExists(id);
        // 删除
        energyDeviceMapper.deleteById(id);

        // 删除子表
        deleteEnergyDeviceCheckRecordByDeviceId(id);
    }

    private void validateEnergyDeviceExists(Long id) {
        if (energyDeviceMapper.selectById(id) == null) {
            throw exception(ENERGY_DEVICE_NOT_EXISTS);
        }
    }

    @Override
    public EnergyDeviceRespVO getEnergyDevice(Long id) {
        EnergyDeviceDO energyDeviceDO = energyDeviceMapper.selectById(id);
        EnergyDeviceRespVO energyDeviceRespVO = BeanUtils.toBean(energyDeviceDO, EnergyDeviceRespVO.class);
        List<OperationRulesVO> operationRulesVOList = JSON.parseArray(energyDeviceDO.getRules(), OperationRulesVO.class);
        energyDeviceRespVO.setOperationRulesVOList(operationRulesVOList);
        return energyDeviceRespVO;
    }

    @Override
    public PageResult<EnergyDeviceDO> getEnergyDevicePage(EnergyDevicePageReqVO pageReqVO) {
        return energyDeviceMapper.selectPage(pageReqVO);
    }

    // ==================== 子表（抄表记录） ====================

    @Override
    public PageResult<EnergyDeviceCheckRecordDO> getEnergyDeviceCheckRecordPage(PageParam pageReqVO, Long deviceId) {
        return energyDeviceCheckRecordMapper.selectPage(pageReqVO, deviceId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createEnergyDeviceCheckRecord(EnergyDeviceCheckRecordDO energyDeviceCheckRecord) {
        if(energyDeviceCheckRecord.getCheckTime()==null)
            energyDeviceCheckRecord.setCheckTime(LocalDateTime.now());
        EnergyDeviceDO energyDeviceDO = energyDeviceMapper.selectById(energyDeviceCheckRecord.getDeviceId());
        // 插入
        ;
        energyDeviceCheckRecord.setLastCheckTime(energyDeviceDO.getLastCheckTime());
        energyDeviceCheckRecord.setLastCheckValue(energyDeviceDO.getLastCheckValue());
        BigDecimal last = energyDeviceDO.getLastCheckValue()==null? BigDecimal.ZERO : energyDeviceDO.getLastCheckValue();

        BigDecimal diff = energyDeviceCheckRecord.getCheckValue().subtract(last).abs();
        energyDeviceCheckRecord.setDiffValue(diff);
        energyDeviceCheckRecordMapper.insert(energyDeviceCheckRecord);

        energyDeviceDO.setLastCheckTime(energyDeviceCheckRecord.getCheckTime());
        energyDeviceDO.setLastCheckValue(energyDeviceCheckRecord.getCheckValue());
        energyDeviceMapper.updateById(energyDeviceDO);
        // 返回
        return energyDeviceCheckRecord.getId();
    }

    @Override
    public void updateEnergyDeviceCheckRecord(EnergyDeviceCheckRecordDO energyDeviceCheckRecord) {
        // 校验存在
        validateEnergyDeviceCheckRecordExists(energyDeviceCheckRecord.getId());
        // 更新
        energyDeviceCheckRecordMapper.updateById(energyDeviceCheckRecord);
    }

    @Override
    public void deleteEnergyDeviceCheckRecord(Long id) {
        // 校验存在
        validateEnergyDeviceCheckRecordExists(id);
        // 删除
        energyDeviceCheckRecordMapper.deleteById(id);
    }

    @Override
    public EnergyDeviceCheckRecordDO getEnergyDeviceCheckRecord(Long id) {
        return energyDeviceCheckRecordMapper.selectById(id);
    }

    private void validateEnergyDeviceCheckRecordExists(Long id) {
        if (energyDeviceCheckRecordMapper.selectById(id) == null) {
            throw exception(ENERGY_DEVICE_CHECK_RECORD_NOT_EXISTS);
        }
    }

    private void deleteEnergyDeviceCheckRecordByDeviceId(Long deviceId) {
        energyDeviceCheckRecordMapper.deleteByDeviceId(deviceId);
    }
    @Override
    public List<EnergyDeviceDO> getList(Collection<Long> ids) {
        return energyDeviceMapper.selectBatchIds(ids);
    }

//    @Override
//    public List<EnergyDeviceRespVO> queryDataRecords(EnergyDeviceConsumptionReqVO req) {
//        List<EnergyDeviceRespVO> result = new ArrayList<>();
//
//        // 查询设备列表
//        List<EnergyDeviceDO> devices = queryDevices(req);
//        if (devices.isEmpty()) return result;
//
//        // 解析规则 & 收集所有 ruleDeviceIds 和点位ID
//        Map<Long, List<OperationRulesVO>> deviceRulesMap = new HashMap<>();
//        Set<Long> ruleDeviceIds = new HashSet<>();
//        Set<Long> pointIds = new HashSet<>();
//        for (EnergyDeviceDO device : devices) {
//            List<OperationRulesVO> rules = parseRules(device);
//            if (!rules.isEmpty()) {
//                deviceRulesMap.put(device.getId(), rules);
//                rules.forEach(r -> {
//                    if (r.getDeviceId() != null) ruleDeviceIds.add(r.getDeviceId());
//                    if (r.getPointId() != null) pointIds.add(r.getPointId());
//                });
//            }
//        }
//
//        if (ruleDeviceIds.isEmpty()) return result;
//
//        // TDengine 查询所有设备首末数据
//        Map<Long, DeviceEdgeData> edgeDataMap =
//                tDengineService.queryDeviceFirstAndLast(ruleDeviceIds, req.getStartTime(), req.getEndTime());
//
//        // 构建缓存
//        Map<Long, TimePointCache> firstCache = buildCache(edgeDataMap, true);
//        Map<Long, TimePointCache> lastCache = buildCache(edgeDataMap, false);
//
//        // 批量获取点位信息（名称/单位）避免 N+1 查询
//        Map<Long, DeviceContactModelDO> pointInfoMap = batchGetPointInfoFromLocalDB(new ArrayList<>(pointIds));
//
//        // 逐设备计算 EnergyDeviceRespVO
//        for (EnergyDeviceDO device : devices) {
//            List<OperationRulesVO> rules = deviceRulesMap.get(device.getId());
//            if (rules == null || rules.isEmpty()) continue;
//
//            EnergyDeviceRespVO vo = calculateDevice(device, rules, firstCache, lastCache, pointInfoMap, req);
//            result.add(vo);
//        }
//
//        return result;
//    }


    @Override
    public List<HourEnergyValueVO> lastEnergyStatistics(Long deviceTypeId, Long orgId) {

        // 1. 找最新的能耗设备
        EnergyDeviceDO energyDevice = energyDeviceMapper.selectOne(
                Wrappers.<EnergyDeviceDO>lambdaQuery()
                        .eq(EnergyDeviceDO::getDeviceTypeId, deviceTypeId)
                        .eq(EnergyDeviceDO::getOrgId, orgId)
                        .orderByDesc(EnergyDeviceDO::getCreateTime)
                        .last("LIMIT 1")
        );

        if (energyDevice == null || StringUtils.isBlank(energyDevice.getRules())) {
            return buildEmptyLast7Hours(LocalDateTime.now());
        }

        // 2. 解析规则
        List<OperationRulesVO> rules = JsonUtils.parseArray(energyDevice.getRules(), OperationRulesVO.class);
        if (rules.isEmpty()) return Collections.emptyList();

        // 3. 收集设备列表
        Set<Long> deviceIds = rules.stream()
                .map(OperationRulesVO::getDeviceId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (deviceIds.isEmpty()) return Collections.emptyList();

        // 4. 查询设备点位信息 DeviceContactModelDO
        List<Long> pointIds = rules.stream()
                .map(OperationRulesVO::getPointId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        Map<Long, DeviceContactModelDO> pointInfoMap = deviceContactModelMapper.selectList(
                Wrappers.<DeviceContactModelDO>lambdaQuery().in(DeviceContactModelDO::getId, pointIds)
        ).stream().collect(Collectors.toMap(DeviceContactModelDO::getId, Function.identity()));

        // 5. 时间范围：最近 7 小时
        LocalDateTime end = LocalDateTime.now();
        LocalDateTime start = end.minusHours(7);
        DateTimeFormatter hourFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");

        // 6. 批量查询 TDengine 数据
        List<Map<String, Object>> allRows = tDengineService.newQueryLastDataByHourBatch(deviceIds, start, end);

        // 7. 按小时 + 设备构建缓存
        Map<String, Map<Long, TimePointCache>> hourCacheMap = new LinkedHashMap<>();

        for (Map<String, Object> row : allRows) {
            LocalDateTime ts = (LocalDateTime) row.get("timestamp");
            if (ts == null) continue;

            String hourKey = ts.format(hourFormatter);
            Long deviceId = (Long) row.get("deviceId");

            // 过滤该设备的规则
            List<OperationRulesVO> deviceRules = rules.stream()
                    .filter(r -> deviceId.equals(r.getDeviceId()))
                    .collect(Collectors.toList());

            TimePointCache cache = new TimePointCache();
            cache.setTimestamp(ts);

            Map<Long, Double> pointValueMap = new HashMap<>();
            for (OperationRulesVO rule : deviceRules) {
                Long pointId = rule.getPointId();
                DeviceContactModelDO pointInfo = pointInfoMap.get(pointId);
                if (pointInfo == null || !deviceId.equals(pointInfo.getDeviceId())) continue;

                String attributeCode = pointInfo.getAttributeCode();
                if (attributeCode == null) continue;

                Object value = row.get(attributeCode.toLowerCase());
                if (value != null) {
                    try {

                        // 保留2位小数
                        BigDecimal bd = new BigDecimal(value.toString());
                        Double finalValue = bd.setScale(2, RoundingMode.HALF_UP).doubleValue();
                        pointValueMap.put(pointId, finalValue);
                    } catch (Exception e) {
                        log.warn("点位值转换失败, pointId={}, value={}, dataType={}", pointId, value, pointInfo.getDataType(), e);
                    }
                }
            }

            cache.setPointValueMap(pointValueMap);

            hourCacheMap.computeIfAbsent(hourKey, k -> new HashMap<>()).put(deviceId, cache);
        }

        // 8. 生成每小时结果，补齐缺失小时
        List<HourEnergyValueVO> result = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            LocalDateTime hourTime = start.plusHours(i);
            String hourKey = hourTime.format(hourFormatter);

            Map<Long, TimePointCache> deviceCache = hourCacheMap.getOrDefault(hourKey, new HashMap<>());

            double total = 0.0;
            for (Long deviceId : deviceIds) {
                total += calculateByRules(deviceId, rules, deviceCache);
            }

            HourEnergyValueVO vo = new HourEnergyValueVO();
            vo.setHour(hourKey);
            vo.setValue(String.valueOf(total));

            result.add(vo);
        }

        return result;
    }



    private List<HourEnergyValueVO> buildEmptyLast7Hours(LocalDateTime end) {
        List<HourEnergyValueVO> result = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
        LocalDateTime start = end.minusHours(7);

        for (int i = 0; i < 7; i++) {
            LocalDateTime hourTime = start.plusHours(i);
            HourEnergyValueVO vo = new HourEnergyValueVO();
            vo.setHour(hourTime.format(formatter));
            vo.setValue(formatDouble(0D));
            result.add(vo);
        }
        return result;
    }

    private List<DayEnergyValueVO> buildEmptyLast7Days(LocalDate endDate) {
        List<DayEnergyValueVO> result = new ArrayList<>();
        int days = 7;
        for (int i = 0; i < days; i++) {
            LocalDate day = endDate.minusDays(days - i).plusDays(1); // 确保顺序从过去到今天
            DayEnergyValueVO vo = new DayEnergyValueVO();
            vo.setDay(day.toString());
            vo.setValue(formatDouble(0D));
            result.add(vo);
        }
        return result;
    }

    @Override
    public List<DayEnergyValueVO> latestSevenDaysStatistics(Long deviceTypeId, Long orgId) {

        // 1. 找最新能耗设备
        EnergyDeviceDO energyDevice = energyDeviceMapper.selectOne(
                Wrappers.<EnergyDeviceDO>lambdaQuery()
                        .eq(EnergyDeviceDO::getDeviceTypeId, deviceTypeId)
                        .eq(EnergyDeviceDO::getOrgId, orgId)
                        .orderByDesc(EnergyDeviceDO::getCreateTime)
                        .last("LIMIT 1")
        );

        if (energyDevice == null || StringUtils.isBlank(energyDevice.getRules())) {
            return buildEmptyLast7Days(LocalDate.now());
        }

        // 2. 解析规则
        List<OperationRulesVO> rules = JsonUtils.parseArray(energyDevice.getRules(), OperationRulesVO.class);
        if (rules.isEmpty()) return Collections.emptyList();

        // 3. 收集设备列表
        Set<Long> deviceIds = rules.stream()
                .map(OperationRulesVO::getDeviceId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (deviceIds.isEmpty()) return Collections.emptyList();

        // 4. 查询设备点位信息 DeviceContactModelDO
        List<Long> pointIds = rules.stream()
                .map(OperationRulesVO::getPointId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());

        Map<Long, DeviceContactModelDO> pointInfoMap = deviceContactModelMapper.selectList(
                Wrappers.<DeviceContactModelDO>lambdaQuery().in(DeviceContactModelDO::getId, pointIds)
        ).stream().collect(Collectors.toMap(DeviceContactModelDO::getId, Function.identity()));

        // 5. 时间范围：最近 7 天
        LocalDate startDate = LocalDate.now().minusDays(7);
        int days = 7;

        // 6. 批量查询 TDengine 数据（按天）
        List<Map<String, Object>> allRows = tDengineService.queryLastDataByDayBatch(deviceIds, startDate, days);

        // 7. 构建按天 + 设备缓存
        Map<String, Map<Long, TimePointCache>> dayCacheMap = new LinkedHashMap<>();

        for (Map<String, Object> row : allRows) {
            String dayKey = (String) row.get("day");
            Long deviceId = (Long) row.get("deviceId");
            LocalDateTime ts = (LocalDateTime) row.get("timestamp");
            if (dayKey == null || deviceId == null || ts == null) continue;

            TimePointCache cache = new TimePointCache();
            cache.setTimestamp(ts);

            Map<Long, Double> pointValueMap = new HashMap<>();

            // 设备对应规则
            List<OperationRulesVO> deviceRules = rules.stream()
                    .filter(r -> deviceId.equals(r.getDeviceId()))
                    .collect(Collectors.toList());

            for (OperationRulesVO rule : deviceRules) {
                Long pointId = rule.getPointId();
                DeviceContactModelDO pointInfo = pointInfoMap.get(pointId);
                if (pointInfo == null || !deviceId.equals(pointInfo.getDeviceId())) continue;

                String attributeCode = pointInfo.getAttributeCode();
                if (attributeCode == null) continue;

                Object value = row.get(attributeCode.toLowerCase());
                if (value != null) {
                    try {
                        BigDecimal bd = new BigDecimal(value.toString());
                        pointValueMap.put(pointId, bd.setScale(2, RoundingMode.HALF_UP).doubleValue());
                    } catch (Exception e) {
                        log.warn("点位值转换失败, pointId={}, value={}, dataType={}", pointId, value, pointInfo.getDataType(), e);
                    }
                }
            }

            cache.setPointValueMap(pointValueMap);
            dayCacheMap.computeIfAbsent(dayKey, k -> new HashMap<>()).put(deviceId, cache);
        }

        // 8. 生成结果，补齐缺失日期
        List<DayEnergyValueVO> result = new ArrayList<>();
        for (int i = 0; i < days; i++) {
            LocalDate day = startDate.plusDays(i);
            String dayKey = day.toString();

            Map<Long, TimePointCache> deviceCache = dayCacheMap.getOrDefault(dayKey, Collections.emptyMap());

            double total = 0.0;
            for (Long deviceId : deviceIds) {
                total += calculateByRules(deviceId, rules, deviceCache);
            }

            DayEnergyValueVO vo = new DayEnergyValueVO();
            vo.setDay(dayKey);
            vo.setValue(String.valueOf(total));

            result.add(vo);
        }

        return result;
    }



    @Override
    public List<EnergyDeviceRespVO> queryDataRecords(EnergyDeviceConsumptionReqVO req) {

        List<EnergyDeviceRespVO> result = new ArrayList<>();

        // 1. 查询能耗设备列表
        List<EnergyDeviceDO> devices = queryDevices(req);

        if (devices.isEmpty()) {
            return result;
        }

        // 2. 解析规则
        Map<Long, List<OperationRulesVO>> deviceRulesMap = new HashMap<>();
        List<Long> ruleDeviceIds = new ArrayList<>();
        List<Long> pointIds = new ArrayList<>();
        for (EnergyDeviceDO device : devices) {
            List<OperationRulesVO> rules = parseRules(device);
            if (rules.isEmpty()) {
                continue;
            }
            deviceRulesMap.put(device.getId(), rules);
            for (OperationRulesVO r : rules) {
                if (r.getDeviceId() != null) {
                    ruleDeviceIds.add(r.getDeviceId());
                }
                if (r.getPointId() != null) {

                    pointIds.add(r.getPointId());
                }
            }
        }

        if (ruleDeviceIds.isEmpty()) {
            return result;
        }

        // 3. 查询每个 device 最早数据
        Map<Long, Map<String, Object>> firstRowMap = tDengineService.queryDevicesEarliestRow(ruleDeviceIds, req.getStartTime(), req.getEndTime());

        // 4. 查询每个 device 最新数据
        Map<Long, Map<String, Object>> lastRowMap = tDengineService.queryDevicesLatestRow(ruleDeviceIds, req.getStartTime(), req.getEndTime());


        // 5. 查询点位信息（名称/单位）
        Map<Long, DeviceContactModelDO> pointInfoMap = batchGetPointInfoFromLocalDB(new ArrayList<>(pointIds));


        // 6. 构建缓存
        Map<Long, TimePointCache> firstCache = buildCacheFromRow(devices, firstRowMap, pointInfoMap);
        Map<Long, TimePointCache> lastCache = buildCacheFromRow(devices, lastRowMap, pointInfoMap);


        // 7. 逐个能耗设备计算
        for (EnergyDeviceDO device : devices) {

            List<OperationRulesVO> rules = deviceRulesMap.get(device.getId());

            if (rules == null || rules.isEmpty()) {

                continue;
            }

            EnergyDeviceRespVO vo = calculateDevice(device, rules, firstCache, lastCache, pointInfoMap, req);
            result.add(vo);
        }

        return result;
    }

    private Map<Long, TimePointCache> buildCacheFromRow(
            List<EnergyDeviceDO> devices,
            Map<Long, Map<String, Object>> rowMap,
            Map<Long, DeviceContactModelDO> pointInfoMap) {

        Map<Long, TimePointCache> cacheMap = new HashMap<>();
        if (devices == null || devices.isEmpty()) return cacheMap;

        for (EnergyDeviceDO device : devices) {

            List<OperationRulesVO> rules = parseRules(device);
            if (rules == null || rules.isEmpty()) continue;

            TimePointCache cache = new TimePointCache();

            Map<Long, Double> valueMap = new HashMap<>();

            // 时间戳变量
            LocalDateTime timestamp = null;

            // 遍历规则
            for (OperationRulesVO rule : rules) {

                Long ruleDeviceId = rule.getDeviceId();
                Long pointId = rule.getPointId();

                if (ruleDeviceId == null || pointId == null)
                    continue;

                DeviceContactModelDO pointInfo = pointInfoMap.get(pointId);

                if (pointInfo == null)
                    continue;

                // 规则设备和点位设备匹配
                if (!ruleDeviceId.equals(pointInfo.getDeviceId()))
                    continue;

                String attributeCode = pointInfo.getAttributeCode();

                if (attributeCode == null)
                    continue;

                // 从 TDengine 数据里取值
                Map<String, Object> row = rowMap.get(ruleDeviceId);

                if (row == null)
                    continue;

                // 设置 timestamp（只需要一次）
                if (timestamp == null) {
                    Object ts = row.get("ts");
                    if (ts instanceof LocalDateTime) {
                        timestamp = (LocalDateTime) ts;
                    }
                }

                Object value = row.get(attributeCode);

                if (value != null) {
                    try {
                        //计算类型统一转换为通用double类型
                        Object converted = JavaToTdengineTypeEnum.convertValue(
                                pointInfo.getDataType(),
                                value.toString()
                        );
                        Double finalValue = convertToDouble(converted);
                        valueMap.put(pointId, finalValue);
                    } catch (Exception e) {
                        log.warn("点位值转换失败, pointId={}, value={}, dataType={}", pointId, value, pointInfo.getDataType(), e);
                    }
                }
            }

            // 设置 timestamp
            cache.setTimestamp(timestamp);

            cache.setPointValueMap(valueMap);

            cacheMap.put(device.getId(), cache);
        }

        return cacheMap;
    }

// =================== 核心方法 ===================

    /** 查询设备列表 */
    private List<EnergyDeviceDO> queryDevices(EnergyDeviceConsumptionReqVO req) {
        return energyDeviceMapper.selectList(
                Wrappers.<EnergyDeviceDO>lambdaQuery()
                        .in(StringUtils.isNotBlank(req.getIds()),
                                EnergyDeviceDO::getId,
                                StringUtils.isNotBlank(req.getIds())
                                        ? Arrays.asList(req.getIds().split(","))
                                        : null)
                        .like(StringUtils.isNotBlank(req.getName()), EnergyDeviceDO::getName, req.getName())
                        .eq(req.getOrgId() != null, EnergyDeviceDO::getOrgId, req.getOrgId())
                        .orderByDesc(EnergyDeviceDO::getCreateTime)
        );
    }

    /** 解析设备规则 */
    private List<OperationRulesVO> parseRules(EnergyDeviceDO device) {
        if (device == null || StringUtils.isBlank(device.getRules())) return Collections.emptyList();
        try {
            return JSON.parseArray(device.getRules(), OperationRulesVO.class);
        } catch (Exception e) {
            log.error("解析规则失败, deviceId={}", device.getId(), e);
            return Collections.emptyList();
        }
    }



    /** 单设备计算 */
    private EnergyDeviceRespVO calculateDevice(EnergyDeviceDO device,
                                               List<OperationRulesVO> rules,
                                               Map<Long, TimePointCache> firstCache,
                                               Map<Long, TimePointCache> lastCache,
                                               Map<Long, DeviceContactModelDO> pointInfoMap,
                                               EnergyDeviceConsumptionReqVO req) {

        Double firstTotal = calculateByRules(device.getId(), rules, firstCache);
        Double lastTotal  = calculateByRules(device.getId(), rules, lastCache);


        EnergyDeviceRespVO vo = new EnergyDeviceRespVO();
        vo.setId(device.getId());
        vo.setName(device.getName());
        vo.setCode(device.getCode());
        vo.setOrgId(device.getOrgId());
        vo.setOrgName(device.getOrgName());
        vo.setCreateTime(device.getCreateTime());
        vo.setUpdateTime(device.getUpdateTime());
        vo.setRules(device.getRules());
        vo.setDeviceTypeName(device.getDeviceTypeName());
        vo.setEnergyConsumption(formatDouble(lastTotal - firstTotal));
        vo.setEarliestDataTime(req.getStartTime());
        vo.setLatestDataTime(req.getEndTime());

        // 构建点位详情
        vo.setPointDetails(buildPointDetails(device.getId(), rules, firstCache, lastCache, pointInfoMap));

        return vo;
    }

    /** 根据规则计算总值 */
    private Double calculateByRules(
            Long energyDeviceId,
            List<OperationRulesVO> rules,
            Map<Long, TimePointCache> cache) {

        TimePointCache c = cache.get(energyDeviceId);

        if (c == null) {
            return 0.0;
        }

        BigDecimal total = null;
        String lastOp = "+";

        for (OperationRulesVO r : rules) {
            Double value = getPointValue(c, r.getPointId());
            if (value == null) continue;

            BigDecimal bdValue = BigDecimal.valueOf(value);

            if (total == null) {
                total = bdValue.setScale(2, RoundingMode.HALF_UP);
            } else {
                total = applyOperator(total, bdValue, StringUtils.defaultIfBlank(r.getOperator(), lastOp));
            }

            lastOp = r.getOperator();
        }

        return total != null ? total.doubleValue() : 0.0;
    }


    /** 延迟解析 JSON 获取点位值 */
    private Double getPointValue(
            TimePointCache cache,
            Long pointId) {

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

        Map<Long, Double> map =
                cache.getPointValueMap();

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

        return map.get(pointId);
    }


    /** 应用运算符 */
    private BigDecimal applyOperator(BigDecimal current, BigDecimal value, String operator) {
        if (current == null || value == null || StringUtils.isBlank(operator)) return current;
        switch (operator.trim()) {
            case "+":
                return current.add(value).setScale(2, RoundingMode.HALF_UP);
            case "-":
                return current.subtract(value).setScale(2, RoundingMode.HALF_UP);
            case "*":
                return current.multiply(value).setScale(2, RoundingMode.HALF_UP);
            case "/":
                return value.abs().compareTo(BigDecimal.valueOf(1e-6)) > 0
                        ? current.divide(value, 2, RoundingMode.HALF_UP)
                        : current;
            default:
                return current.add(value).setScale(2, RoundingMode.HALF_UP);
        }
    }


    /** 构建点位详情列表并使用本地数据库批量获取点位信息 */
    private List<PointDetailVO> buildPointDetails(
            Long energyDeviceId,
            List<OperationRulesVO> rules,
            Map<Long, TimePointCache> firstCache,
            Map<Long, TimePointCache> lastCache,
            Map<Long, DeviceContactModelDO> pointInfoMap) {

        List<PointDetailVO> result = new ArrayList<>();
        if (rules == null || rules.isEmpty()) return result;

        TimePointCache first = firstCache.get(energyDeviceId);
        TimePointCache last  = lastCache.get(energyDeviceId);

        for (OperationRulesVO r : rules) {

            PointDetailVO p = new PointDetailVO();

            p.setDeviceId(r.getDeviceId());
            p.setPointId(r.getPointId());
            p.setOperator(r.getOperator());

            Double firstVal = getPointValue(first, r.getPointId());
            Double lastVal  = getPointValue(last, r.getPointId());

            p.setEarliestValue(formatDouble(firstVal));
            p.setLatestValue(formatDouble(lastVal));

            p.setDifference(formatDouble(
                    (lastVal != null ? lastVal : 0.0)
                            - (firstVal != null ? firstVal : 0.0)
            ));

            if (first != null)
                p.setEarliestTime(formatToSecond(first.getTimestamp()));

            if (last != null)
                p.setLatestTime(formatToSecond(last.getTimestamp()));

            DeviceContactModelDO pointInfo = pointInfoMap.get(r.getPointId());

            if (pointInfo != null) {

                p.setPointName(
                        StringUtils.isNotBlank(pointInfo.getAttributeName())
                                ? pointInfo.getAttributeName()
                                : pointInfo.getAttributeCode()
                );

                p.setUnit(pointInfo.getDataUnit());
            }

            result.add(p);
        }

        return result;
    }


    private String formatToSecond(Object timestamp) {
        if (timestamp == null) {
            return "";
        }
        try {
            String ts = timestamp.toString();

            // ISO: 2026-01-22T17:18:50.092
            if (ts.contains("T")) {
                ts = ts.replace("T", " ");
            }

            // 去掉毫秒
            int dotIndex = ts.indexOf(".");
            if (dotIndex > 0) {
                ts = ts.substring(0, dotIndex);
            }

            return ts;
        } catch (Exception e) {
            return timestamp.toString();
        }
    }

    /** 格式化 double */
    private String formatDouble(Double value) {
        if (value == null) return "0.0";
        return new DecimalFormat("#.##").format(value);
    }

    /** 批量获取点位信息（本地数据库） */
    private Map<Long, DeviceContactModelDO> batchGetPointInfoFromLocalDB(List<Long> pointIds) {
        Map<Long, DeviceContactModelDO> result = new HashMap<>();
        if (pointIds == null || pointIds.isEmpty()) return result;

        try {
            List<DeviceContactModelDO> contacts = deviceContactModelMapper.selectBatchIds(pointIds);
            if (contacts != null) {
                for (DeviceContactModelDO c : contacts) {
                    if (c != null && c.getId() != null) result.put(c.getId(), c);
                }
            }
        } catch (Exception e) {
            log.error("批量查询点位信息失败, pointIds: {}", pointIds, e);
        }

        return result;
    }


    private Double convertToDouble(Object value) {
        if (value == null) return null;
        if (value instanceof Number) return ((Number) value).doubleValue();
        if (value instanceof Boolean) return (Boolean) value ? 1.0 : 0.0;
        if (value instanceof Character) return (double) ((Character) value);
        return Double.valueOf(value.toString());
    }


}