/*
 * 文 件 名:  ChargingDataService
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  充电数据服务
 * 修 改 人:  lvwenbin
 * 修改时间:  2026/1/9
 * 修改内容:  使用统一设备映射缓存 ChargingDeviceMappingCache 替代原有分散缓存
 */
package com.ruoyi.ems.charging.service;

import com.huashe.common.exception.Assert;
import com.huashe.common.exception.BusinessException;
import com.ruoyi.ems.charging.model.ChargingSession;
import com.ruoyi.ems.charging.model.RealtimeDataCache;
import com.ruoyi.ems.charging.model.req.ChargingEndFrame;
import com.ruoyi.ems.charging.model.req.ChargingHandshakeFrame;
import com.ruoyi.ems.charging.model.req.ErrorReportFrame;
import com.ruoyi.ems.charging.model.req.LoginReqFrame;
import com.ruoyi.ems.charging.model.req.RealtimeDataFrame;
import com.ruoyi.ems.charging.model.req.RemoteStopRespFrame;
import com.ruoyi.ems.charging.model.req.TransactionRecordFrame;
import com.ruoyi.ems.charging.model.req.WorkParamSetRespFrame;
import com.ruoyi.ems.domain.EmsDevice;
import com.ruoyi.ems.domain.EmsObjAttrValue;
import com.ruoyi.ems.enums.DevOnlineStatus;
import com.ruoyi.ems.service.IEmsDeviceService;
import com.ruoyi.ems.service.IEmsObjAttrValueService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 充电数据服务
 * 处理充电桩上报的各类数据，转化为能耗数据存储
 *
 * @author lvwenbin
 * @version [版本号, 2026/1/9]
 */
@Service
public class ChargingDataService {

    private static final Logger log = LoggerFactory.getLogger(ChargingDataService.class);

    /**
     * 充电主机模型代码
     */
    private static final String MODEL_CODE_HOST = "M_W2_DEV_CHARGING_HOST";

    /**
     * 充电枪模型代码
     */
    private static final String MODEL_CODE_PILE = "M_W2_DEV_CHARGING_PILE";

    /**
     * 充电系统模型代码
     */
    private static final String MODEL_CODE_SYS = "M_W2_SYS_CHARGING";

    /**
     * 子设备属性键
     */
    private static final String ATTR_KEY_SUB_DEV = "subDev";

    /**
     * 桩编码属性键
     */
    private static final String ATTR_KEY_PILE_CODE = "pileCode";

    /**
     * 缓存过期时间(小时) - 超过此时间未更新的缓存将被清理
     */
    private static final int CACHE_EXPIRE_HOURS = 24;

    /**
     * 实时数据缓存 - 枪号 -> 最新实时数据
     */
    private final Map<String, RealtimeDataCache> realtimeDataCache = new ConcurrentHashMap<>();

    /**
     * 充电会话缓存 - 交易流水号 -> 充电会话
     */
    private final Map<String, ChargingSession> chargingSessionCache = new ConcurrentHashMap<>();

    /**
     * 远程停机请求回调 - 枪号 -> 回调时间（用于超时检测）
     */
    private final Map<String, LocalDateTime> pendingStopRequests = new ConcurrentHashMap<>();

    @Autowired
    private IEmsObjAttrValueService objAttrValueService;

    @Autowired
    protected IEmsDeviceService deviceService;

    @Autowired
    private ChargingDeviceMappingCache deviceMappingCache;

    @Autowired
    private ChargingMeterService chargingMeterService;

    // ==================== 消息处理方法 ====================

    /**
     * 处理充电桩登录
     * 根据桩编码更新充电主机的物模型属性
     * 同时更新子设备（充电枪）的在线状态
     */
    @Async("msgExecExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void onPileLogin(LoginReqFrame req, boolean valid) {
        if (!valid) {
            log.warn("充电桩登录验证失败，不进行属性更新 - 桩号: {}", req.getPileCode());
            return;
        }

        log.info("充电桩登录成功，开始同步属性 - 桩号: {}, 类型: {}, 枪数: {}, 协议版本: {}, 软件版本: {}",
            req.getPileCode(), req.getPileTypeDesc(), req.getGunCount(), req.getProtocolVersionDesc(),
            req.getSoftwareVersion());

        try {
            // 使用统一缓存查询设备编码
            String deviceCode = deviceMappingCache.getHostDeviceCode(req.getPileCode());
            if (StringUtils.isBlank(deviceCode)) {
                log.warn("未找到桩编码 {} 对应的充电主机设备", req.getPileCode());
                return;
            }

            log.info("找到充电主机设备: {} -> {}", req.getPileCode(), deviceCode);

            // 1. 更新充电主机在线状态
            EmsDevice emsDevice = deviceService.selectByCode(deviceCode);
            Assert.notNull(emsDevice, -1, String.format("未找到设备编码 %s 对应的充电主机设备", deviceCode));

            if (emsDevice.getDeviceStatus() == null
                || emsDevice.getDeviceStatus() == DevOnlineStatus.OFFLINE.getCode()) {
                emsDevice.setDeviceStatus(DevOnlineStatus.ONLINE.getCode());
                deviceService.updateEmsDevice(emsDevice);
                log.info("充电主机 {} 状态更新为在线", deviceCode);
            }

            // 2. 更新子设备（充电枪）在线状态
            updateSubDevicesOnlineStatus(deviceCode, DevOnlineStatus.ONLINE);

            // 3. 获取现有属性，构建属性Map
            List<EmsObjAttrValue> existingAttrs = objAttrValueService.selectByObjCode(MODEL_CODE_HOST, deviceCode);
            Map<String, EmsObjAttrValue> attrMap = existingAttrs.stream()
                .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));

            // 4. 更新属性值
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_HOST, "gunCount", String.valueOf(req.getGunCount()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_HOST, "protocolVersion", req.getProtocolVersionDesc());
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_HOST, "softwareVersion", req.getSoftwareVersion());
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_HOST, "networkType", req.getNetworkTypeDesc());
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_HOST, "simCard", req.getSimCard());
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_HOST, "carrier", req.getCarrierDesc());

            log.info("充电主机 {} 属性同步完成", deviceCode);
        }
        catch (BusinessException e) {
            log.warn(e.getMessage());
        }
        catch (Exception e) {
            log.error("充电桩登录属性同步异常 - 桩号: {}", req.getPileCode(), e);
        }
    }

    /**
     * 处理充电桩断开连接
     * 将充电主机和其下所有充电枪标记为离线
     *
     * @param pileCode 桩编码
     */
    @Async("msgExecExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void onPileDisconnect(String pileCode) {
        if (StringUtils.isBlank(pileCode)) {
            log.warn("充电桩断开连接，但桩编码为空");
            return;
        }

        log.info("充电桩断开连接，开始更新设备状态 - 桩号: {}", pileCode);

        try {
            // 使用统一缓存查询设备编码
            String deviceCode = deviceMappingCache.getHostDeviceCode(pileCode);
            if (StringUtils.isBlank(deviceCode)) {
                log.warn("充电桩断开连接，但未找到对应的设备编码 - 桩号: {}", pileCode);
                return;
            }

            // 2. 更新充电主机离线状态
            EmsDevice hostDevice = deviceService.selectByCode(deviceCode);
            if (hostDevice != null) {
                hostDevice.setDeviceStatus(DevOnlineStatus.OFFLINE.getCode());
                deviceService.updateEmsDevice(hostDevice);
                log.info("充电主机 {} 状态更新为离线", deviceCode);
            }

            // 3. 更新子设备（充电枪）离线状态
            updateSubDevicesOnlineStatus(deviceCode, DevOnlineStatus.OFFLINE);

            // 4. 清理桩下线缓存
            onPileOffline(pileCode);

            log.info("充电桩 {} 断开连接处理完成", pileCode);
        }
        catch (Exception e) {
            log.error("充电桩断开连接处理异常 - 桩号: {}", pileCode, e);
        }
    }

    /**
     * 更新子设备（充电枪）的在线状态
     *
     * @param hostDeviceCode 充电主机设备编码
     * @param status         目标状态
     */
    private void updateSubDevicesOnlineStatus(String hostDeviceCode, DevOnlineStatus status) {
        try {
            String pileCode = deviceMappingCache.getProtocolCode(hostDeviceCode);
            if (StringUtils.isBlank(pileCode)) {
                log.warn("未找到充电主机 {} 的桩编码", hostDeviceCode);
                return;
            }

            ChargingDeviceMappingCache.HostDeviceMapping hostMapping = deviceMappingCache.getHostMapping(pileCode);

            if (hostMapping == null || CollectionUtils.isEmpty(hostMapping.getSubDeviceCodes())) {
                log.warn("充电主机 {} 未配置子设备列表", hostDeviceCode);
                return;
            }

            int updateCount = 0;
            for (String subDeviceCode : hostMapping.getSubDeviceCodes()) {
                EmsDevice pileDevice = deviceService.selectByCode(subDeviceCode);
                if (pileDevice != null) {
                    // 仅在状态不同时才更新
                    if (pileDevice.getDeviceStatus() == null || pileDevice.getDeviceStatus() != status.getCode()) {
                        pileDevice.setDeviceStatus(status.getCode());
                        deviceService.updateEmsDevice(pileDevice);
                        updateCount++;
                        log.debug("充电枪 {} 状态更新为 {}", subDeviceCode, status.name());
                    }
                }
                else {
                    log.warn("未找到充电枪设备: {}", subDeviceCode);
                }
            }

            log.info("充电主机 {} 的 {} 个子设备状态已更新为 {}", hostDeviceCode, updateCount, status.name());

        }
        catch (Exception e) {
            log.error("更新充电主机 {} 的子设备状态失败", hostDeviceCode, e);
        }
    }

    /**
     * 处理枪故障
     */
    @Async("msgExecExecutor")
    public void onGunFault(String pileCode, String gunNo) {
        String fullGunNo = pileCode + gunNo;
        log.warn("充电枪故障告警 - 枪号: {}", fullGunNo);

        try {
            String deviceCode = deviceMappingCache.getGunDeviceCode(fullGunNo);
            if (StringUtils.isBlank(deviceCode)) {
                log.warn("未找到充电枪设备 - 完整枪号: {}", fullGunNo);
                return;
            }

            // 更新故障状态
            objAttrValueService.updateObjAttrValue(MODEL_CODE_PILE, deviceCode, "status", "1");
            log.info("充电枪 {} 故障状态已更新", deviceCode);

        }
        catch (Exception e) {
            log.error("充电枪故障状态更新异常 - 枪号: {}", fullGunNo, e);
        }
    }

    /**
     * 处理实时监测数据 - 能耗数据核心处理
     * 同步更新充电枪的物模型属性
     */
    @Async("msgExecExecutor")
    public void processRealtimeData(RealtimeDataFrame data) {
        String fullGunNo = data.getFullGunNo();
        LocalDateTime now = LocalDateTime.now();

        // 更新内存缓存
        RealtimeDataCache cache = realtimeDataCache.computeIfAbsent(fullGunNo, k -> new RealtimeDataCache());
        cache.setLastData(data);
        cache.setUpdateTime(now);

        if (data.isCharging()) {
            processChargingData(data, cache, now);
        }

        // 状态变化日志
        if (cache.getLastStatus() != data.getStatus()) {
            log.info("充电枪状态变化 - 枪号: {}, 状态: {} -> {}", fullGunNo, getStatusDesc(cache.getLastStatus()),
                getStatusDesc(data.getStatus()));
            cache.setLastStatus(data.getStatus());
        }

        // 同步物模型属性
        syncGunRealtimeAttr(data);

        // 保存实时数据到数据库
        saveRealtimeData(data);
    }

    /**
     * 同步充电枪实时数据到物模型属性表
     */
    private void syncGunRealtimeAttr(RealtimeDataFrame data) {
        try {
            // 使用统一缓存查询设备编码
            String fullGunNo = data.getFullGunNo();
            String deviceCode = deviceMappingCache.getGunDeviceCode(fullGunNo);

            if (StringUtils.isBlank(deviceCode)) {
                log.debug("未找到充电枪设备 - 完整枪号: {}", fullGunNo);
                return;
            }

            // 获取现有属性，构建属性Map
            List<EmsObjAttrValue> existingAttrs = objAttrValueService.selectByObjCode(MODEL_CODE_PILE, deviceCode);
            Map<String, EmsObjAttrValue> attrMap = existingAttrs.stream()
                .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));

            // 更新State分组属性
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "transactionNo", data.getTransactionNo());
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "status", data.getStatusEnum());
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "gunReturned", String.valueOf(data.getGunReturned()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "gunConnected",
                String.valueOf(data.getGunConnected()));

            // 更新Measure分组属性
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "outputVoltage",
                String.format("%.1f", data.getOutputVoltageValue()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "outputCurrent",
                String.format("%.1f", data.getOutputCurrentValue()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "gunTemperature",
                String.valueOf(data.getGunTemperatureValue()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "chargingTime",
                String.valueOf(data.getChargingTime()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "remainingTime",
                String.valueOf(data.getRemainingTime()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "chargingEnergy",
                String.format("%.4f", data.getChargingEnergyValue()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "lossCorrectedEnergy",
                String.format("%.4f", data.getLossCorrectedEnergyValue()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "chargedAmount",
                String.format("%.4f", data.getChargedAmountValue()));
            checkAndUpdate(attrMap, deviceCode, MODEL_CODE_PILE, "hardwareFault", data.getHardwareFaultDesc());

            log.debug("充电枪 {} 实时属性同步完成", deviceCode);

        }
        catch (Exception e) {
            log.error("同步充电枪实时属性异常 - 枪号: {}", data.getFullGunNo(), e);
        }
    }

    // ==================== 充电会话处理 ====================

    private void processChargingData(RealtimeDataFrame data, RealtimeDataCache cache, LocalDateTime now) {
        String transactionNo = data.getTransactionNo();

        ChargingSession session = chargingSessionCache.computeIfAbsent(transactionNo, k -> {
            ChargingSession newSession = new ChargingSession();
            newSession.setTransactionNo(transactionNo);
            newSession.setFullGunNo(data.getFullGunNo());
            newSession.setStartTime(now);
            newSession.setStartEnergy(data.getChargingEnergyValue());
            log.info("新充电会话开始 - 流水号: {}, 枪号: {}", transactionNo, data.getFullGunNo());
            return newSession;
        });

        session.setLastUpdateTime(now);
        session.setCurrentEnergy(data.getChargingEnergyValue());
        session.setCurrentPower(data.getOutputPower());
        session.setCurrentSoc(data.getSoc());
        session.setChargingTime(data.getChargingTime());

        double energyDelta = data.getChargingEnergyValue() - cache.getLastEnergy();
        if (energyDelta > 0) {
            session.setTotalEnergy(session.getTotalEnergy() + energyDelta);
        }

        cache.setLastEnergy(data.getChargingEnergyValue());
    }

    private void saveRealtimeData(RealtimeDataFrame data) {
        if (log.isDebugEnabled()) {
            log.debug("保存实时数据 - 枪号: {}, 状态: {}, 功率: {}kW, 电量: {}kWh", data.getFullGunNo(),
                getStatusDesc(data.getStatus()), String.format("%.2f", data.getOutputPower()),
                String.format("%.4f", data.getChargingEnergyValue()));
        }

        // TODO: 实际保存到数据库的逻辑（如需保存历史记录）
    }

    /**
     * 处理充电握手
     */
    @Async("msgExecExecutor")
    public void onChargingHandshake(ChargingHandshakeFrame handshake) {
        log.info("充电握手 - 流水号: {}, 枪号: {}, VIN: {}, 电池类型: {}, 容量: {}Ah", handshake.getTransactionNo(),
            handshake.getFullGunNo(), handshake.getVin(), handshake.getBatteryTypeDesc(),
            String.format("%.1f", handshake.getRatedCapacityValue()));

        ChargingSession session = chargingSessionCache.get(handshake.getTransactionNo());
        if (session != null) {
            session.setVin(handshake.getVin());
            session.setBatteryType(handshake.getBatteryTypeDesc());
            session.setRatedCapacity(handshake.getRatedCapacityValue());
        }
    }

    /**
     * 处理充电结束
     */
    @Async("msgExecExecutor")
    public void onChargingEnd(ChargingEndFrame end) {
        String transactionNo = end.getTransactionNo();

        log.info("充电结束 - 流水号: {}, 枪号: {}, SOC: {}%, 能量: {}kWh, 时长: {}min", transactionNo,
            end.getFullGunNo(), end.getBmsFinalSoc(), String.format("%.1f", end.getOutputEnergyValue()),
            end.getTotalChargingTime());

        ChargingSession session = chargingSessionCache.get(transactionNo);
        if (session != null) {
            session.setEndTime(LocalDateTime.now());
            session.setFinalSoc(end.getBmsFinalSoc());
            session.setTotalEnergy(end.getOutputEnergyValue());
            session.setChargingTime(end.getTotalChargingTime());
        }

        RealtimeDataCache cache = realtimeDataCache.get(end.getFullGunNo());
        if (cache != null) {
            cache.setLastEnergy(0);
        }

        // 更新枪状态为空闲
        try {
            // 使用统一缓存查询设备编码
            String deviceCode = deviceMappingCache.getGunDeviceCode(end.getFullGunNo());
            if (StringUtils.isNotBlank(deviceCode)) {
                objAttrValueService.updateObjAttrValue(MODEL_CODE_PILE, deviceCode, "status", "2");
                log.info("充电枪 {} 状态更新为空闲", deviceCode);
            }
        }
        catch (Exception e) {
            log.error("更新充电结束状态异常", e);
        }
    }

    /**
     * 处理错误报文
     */
    @Async("msgExecExecutor")
    public void onErrorReport(ErrorReportFrame error) {
        if (error.hasError()) {
            log.warn("充电错误报文 - 流水号: {}, 枪号: {}, 错误: {}", error.getTransactionNo(), error.getFullGunNo(),
                error.getErrorSummary());
        }
    }

    /**
     * 处理远程停机应答
     */
    @Async("msgExecExecutor")
    public void onRemoteStopResp(RemoteStopRespFrame resp) {
        String fullGunNo = resp.getFullGunNo();
        pendingStopRequests.remove(fullGunNo);

        if (resp.isSuccess()) {
            log.info("远程停机成功 - 枪号: {}", fullGunNo);
        }
        else {
            log.warn("远程停机失败 - 枪号: {}, 原因: {}", fullGunNo, resp.getFailReasonDesc());
        }
    }

    /**
     * 处理交易记录
     */
    @Async("msgExecExecutor")
    public void onTransactionRecord(TransactionRecordFrame record) {
        String transactionNo = record.getTransactionNo();
        String fullGunNo = record.getFullGunNo();

        log.info("处理交易记录 - 流水号: {}, 枪号: {}", transactionNo, fullGunNo);

        // 打印分时电量汇总
        log.info("分时电量统计 - 尖: {}kWh, 峰: {}kWh, 平: {}kWh, 谷: {}kWh, 总计: {}kWh",
            String.format("%.4f", record.getSharpEnergyValue()),
            String.format("%.4f", record.getPeakEnergyValue()),
            String.format("%.4f", record.getFlatEnergyValue()),
            String.format("%.4f", record.getValleyEnergyValue()),
            String.format("%.4f", record.getTotalEnergyValue()));

        log.info("电表读数 - 起值: {}kWh, 止值: {}kWh, 差值: {}kWh",
            String.format("%.4f", record.getMeterStartValueKwh()),
            String.format("%.4f", record.getMeterEndValueKwh()),
            String.format("%.4f", record.getMeterDeltaKwh()));

        log.info("充电时段 - 开始: {}, 结束: {}, 时长: {}分钟",
            record.getStartTime(), record.getEndTime(), record.getChargingDurationMinutes());

        if (!record.isNormalStop()) {
            log.warn("充电非正常结束 - 流水号: {}, 原因: {}", transactionNo, record.getStopReasonDesc());
        }

        // 【核心改造】保存交易记录和分时计量数据
        try {
            boolean saveResult = chargingMeterService.saveTransactionRecord(record);
            if (saveResult) {
                log.info("交易记录保存成功 - 流水号: {}", transactionNo);
            } else {
                log.error("交易记录保存失败 - 流水号: {}", transactionNo);
            }
        } catch (Exception e) {
            log.error("保存交易记录异常 - 流水号: {}", transactionNo, e);
        }

        // 清理充电会话缓存
        ChargingSession session = chargingSessionCache.remove(transactionNo);
        if (session != null) {
            log.debug("清理充电会话缓存 - 流水号: {}", transactionNo);
        }
    }



    /**
     * 处理工作参数设置应答
     */
    @Async("msgExecExecutor")
    public void onWorkParamSetResp(WorkParamSetRespFrame resp) {
        log.info("工作参数设置应答 - 桩号: {}, 结果: {}", resp.getPileCode(), resp.isSuccess() ? "成功" : "失败");
    }

    /**
     * 记录远程停机请求
     */
    public void recordStopRequest(String fullGunNo) {
        pendingStopRequests.put(fullGunNo, LocalDateTime.now());
    }

    // ==================== 缓存管理 ====================

    /**
     * 定期清理过期缓存
     */
    @Scheduled(fixedRate = 3600000)
    public void cleanExpiredCache() {
        LocalDateTime expireTime = LocalDateTime.now().minus(CACHE_EXPIRE_HOURS, ChronoUnit.HOURS);
        int cleanedRealtimeCount = 0;
        int cleanedSessionCount = 0;

        Iterator<Map.Entry<String, RealtimeDataCache>> realtimeIterator = realtimeDataCache.entrySet().iterator();
        while (realtimeIterator.hasNext()) {
            Map.Entry<String, RealtimeDataCache> entry = realtimeIterator.next();
            RealtimeDataCache cache = entry.getValue();
            if (cache.getUpdateTime() != null && cache.getUpdateTime().isBefore(expireTime)) {
                realtimeIterator.remove();
                cleanedRealtimeCount++;
            }
        }

        Iterator<Map.Entry<String, ChargingSession>> sessionIterator = chargingSessionCache.entrySet().iterator();
        while (sessionIterator.hasNext()) {
            Map.Entry<String, ChargingSession> entry = sessionIterator.next();
            ChargingSession session = entry.getValue();
            if (session.getLastUpdateTime() != null && session.getLastUpdateTime().isBefore(expireTime)) {
                sessionIterator.remove();
                cleanedSessionCount++;
                log.warn("清理超时充电会话 - 流水号: {}, 枪号: {}", session.getTransactionNo(), session.getFullGunNo());
            }
        }

        LocalDateTime stopExpireTime = LocalDateTime.now().minus(5, ChronoUnit.MINUTES);
        pendingStopRequests.entrySet().removeIf(entry -> entry.getValue().isBefore(stopExpireTime));

        if (cleanedRealtimeCount > 0 || cleanedSessionCount > 0) {
            log.info("缓存清理完成 - 清理实时数据缓存: {}条, 充电会话缓存: {}条", cleanedRealtimeCount,
                cleanedSessionCount);
        }
    }

    /**
     * 桩下线清理缓存
     */
    public void onPileOffline(String pileCode) {
        int cleanedCount = 0;

        Iterator<Map.Entry<String, RealtimeDataCache>> iterator = realtimeDataCache.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, RealtimeDataCache> entry = iterator.next();
            if (entry.getKey().startsWith(pileCode)) {
                iterator.remove();
                cleanedCount++;
            }
        }

        if (cleanedCount > 0) {
            log.info("桩下线清理缓存 - 桩号: {}, 清理枪缓存: {}条", pileCode, cleanedCount);
        }
    }

    /**
     * 清除设备编码缓存（设备配置变更时调用）
     */
    public void clearDeviceCodeCache() {
        deviceMappingCache.refreshCache();
        log.info("设备映射缓存已刷新");
    }

    /**
     * 检查并更新属性值
     */
    private void checkAndUpdate(Map<String, EmsObjAttrValue> objAttrValueMap, String objCode, String modelCode,
        String key, String newValue) {
        if (StringUtils.isBlank(newValue)) {
            return;
        }

        EmsObjAttrValue objAttrValue = objAttrValueMap.get(key);

        if (null != objAttrValue) {
            // 值有变化才更新
            if (!StringUtils.equals(objAttrValue.getAttrValue(), newValue)) {
                objAttrValueService.updateObjAttrValue(modelCode, objCode, key, newValue);
            }
        }
        else {
            // 新增属性值
            objAttrValue = new EmsObjAttrValue(objCode, modelCode, key, newValue);
            objAttrValueService.mergeObjAttrValue(objAttrValue);
        }
    }

    private String getStatusDesc(byte status) {
        switch (status) {
            case 0x00:
                return "离线";
            case 0x01:
                return "故障";
            case 0x02:
                return "空闲";
            case 0x03:
                return "充电中";
            case 0x04:
                return "空闲-脉冲静置";
            case 0x05:
                return "充电中-脉冲检测";
            default:
                return "未知";
        }
    }

    // ==================== 查询方法 ====================

    public RealtimeDataCache getRealtimeDataCache(String fullGunNo) {
        return realtimeDataCache.get(fullGunNo);
    }

    public ChargingSession getChargingSession(String transactionNo) {
        return chargingSessionCache.get(transactionNo);
    }

    public Map<String, Integer> getCacheStats() {
        Map<String, Integer> stats = new ConcurrentHashMap<>();
        stats.put("realtimeDataCacheSize", realtimeDataCache.size());
        stats.put("chargingSessionCacheSize", chargingSessionCache.size());
        stats.put("pendingStopRequestsSize", pendingStopRequests.size());

        // 添加设备映射缓存统计
        Map<String, Object> mappingStats = deviceMappingCache.getCacheStats();
        stats.put("hostMappingsSize", (Integer) mappingStats.get("hostMappings"));
        stats.put("gunMappingsSize", (Integer) mappingStats.get("gunMappings"));

        return stats;
    }
}