package com.usky.sas.mqtt;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.usky.sas.common.enums.SystemTypeCodeEnum;
import com.usky.sas.common.global.SasWebSocket;
import com.usky.sas.domain.*;
import com.usky.sas.mapper.*;
import com.usky.sas.mqtt.dto.*;
import com.usky.sas.mqtt.dto.EntranceEventInfoVo;
import com.usky.sas.service.*;
import com.usky.sas.service.dto.agbox.JsonRpcRequest;
import com.usky.sas.service.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MqttService {

    @Value("${mqtt.client-id:sas-client-${random.uuid}}")
    private String clientId;

    @Value("${mqtt.topics:}")
    private String topics;

    private MqttClient client;
    private volatile boolean isListening = true;

    private final ObjectMapper mapper = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Autowired
    private SasUsbEventService usbEventService;
    @Autowired
    private SasSnapEventService snapEventService;
    @Autowired
    private SasEntranceEventService entranceEventService;
    @Autowired
    private SasParkingEventService parkingEventService;
    @Autowired
    private SasRoadblockEventService roadblockEventService;
    @Autowired
    private SasAlarsasEventService alarsasEventService;
    @Autowired
    private SasPatrolEventService patrolEventService;
    @Autowired
    private SasAcquisitionEventService acquisitionEventService;
    @Autowired
    private SasPerceptionEventService perceptionEventService;
    @Autowired
    private SasCollectionEventService collectionEventService;

    @Autowired
    private SasConfigService sasConfigService;
    @Autowired
    private SasDeviceService deviceService;

    @Autowired
    private SasEventTypeGroupService eventTypeGroupService;
    @Autowired
    private SasPatrolUserParamService sasPatrolUserParamService;
    @Autowired
    private SasGisMapper gisMapper;
    
    @Autowired
    private SasPicMapper sasPicMapper;

    // Event Code Mappers
    @Autowired
    private SasUsbEventCodeMapper usbEventCodeMapper;
    @Autowired
    private SasSnapTypeCodeMapper snapTypeCodeMapper;
    @Autowired
    private SasEntranceEventCodeMapper entranceEventCodeMapper;
    @Autowired
    private SasParkingEventCodeMapper parkingEventCodeMapper;
    @Autowired
    private SasRoadblockEventCodeMapper roadblockEventCodeMapper;
    @Autowired
    private SasAlarsasEventCodeMapper alarsasEventCodeMapper;
    @Autowired
    private SasPatrolEventCodeMapper patrolEventCodeMapper;
    @Autowired
    private SasAcquisitionEventCodeMapper acquisitionEventCodeMapper;
    @Autowired
    private SasPerceptionEventCodeMapper perceptionEventCodeMapper;
    @Autowired
    private SasCollectionEventCodeMapper collectionEventCodeMapper;


    @PostConstruct
    public void init() {
        SasConfig config = sasConfigService.getConfig();
        if (config == null || config.getHost() == null || config.getHost().isEmpty()) {
            log.warn("sas_config 中无 MQTT 配置或 host 为空，跳过 MQTT 连接");
            isListening = false;
            return;
        }
        try {
            String host = config.getHost();
            int port = parsePort(config.getPort(), 1883);
            String username = config.getUsername() != null ? config.getUsername() : "";
            String password = config.getPassword() != null ? config.getPassword() : "";

            String brokerUrl = "tcp://" + host + ":" + port;
            client = new MqttClient(brokerUrl, clientId);

            MqttConnectOptions options = new MqttConnectOptions();
            options.setCleanSession(true);
            if (!username.isEmpty()) {
                options.setUserName(username);
                options.setPassword(password.toCharArray());
            }
            options.setConnectionTimeout(60);
            options.setKeepAliveInterval(60);
            options.setAutomaticReconnect(true);

            client.setCallback(new MqttCallbackExtended() {
                @Override
                public void connectComplete(boolean reconnect, String serverURI) {
                    log.info("MQTT连接成功, reconnect={}", reconnect);
                    parseTopics().forEach(MqttService.this::subscribe);
                    isListening = true;
                }

                @Override
                public void connectionLost(Throwable cause) {
                    log.error("MQTT连接丢失: {}", cause.getMessage());
                    isListening = false;
                    scheduleReconnect();
                }

                @Override
                public void messageArrived(String topic, MqttMessage message) throws Exception {
                    if (isListening) {
                        processMessage(topic, message);
                    }
                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {
                }
            });

            client.connect(options);
            log.info("MQTT客户端初始化完成, clientId={}", clientId);
            isListening = true;

        } catch (MqttException e) {
            log.error("MQTT初始化失败: {}", e.getMessage(), e);
            isListening = false;
        }
    }

    private static int parsePort(String portStr, int defaultPort) {
        if (portStr == null || portStr.isEmpty()) {
            return defaultPort;
        }
        try {
            return Integer.parseInt(portStr.trim());
        } catch (NumberFormatException e) {
            return defaultPort;
        }
    }

    private void scheduleReconnect() {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(5000L);
                if (client != null && !client.isConnected()) {
                    log.info("尝试主动重连MQTT...");
                    client.reconnect();
                }
            } catch (Exception e) {
                log.error("MQTT主动重连失败", e);
            }
        }, "mqtt-reconnect");
        t.setDaemon(true);
        t.start();
    }

    private List<String> parseTopics() {
        return Arrays.asList(topics.split(","));
    }

    private void processMessage(String topic, MqttMessage message) {
        String payload = new String(message.getPayload());
        log.debug("收到MQTT消息, topic={}, payload={}", topic, payload);

        try {
            String[] parts = topic.split("/");
            if (parts.length < 2) {
                log.warn("未知的topic格式: {}", topic);
                return;
            }

            String eventType = null;
            for (int i = 0; i < parts.length - 1; i++) {
                if ("event".equals(parts[i]) && i + 1 < parts.length) {
                    eventType = parts[i + 1];
                    break;
                }
            }

            if (eventType == null) {
                log.warn("无法从topic解析事件类型: {}", topic);
                return;
            }

            switch (eventType) {
                case "usbalarm":
                    handleUsbEvent(payload, topic);
                    break;
                case "snap":
                    handleSnapEvent(payload, topic);
                    break;
                case "entrance":
                    handleEntranceEvent(payload, topic);
                    break;
                case "parking":
                    handleParkingEvent(payload, topic);
                    break;
                case "roadblock":
                    handleRoadblockEvent(payload, topic);
                    break;
                case "alarm":
                    handleAlarsasEvent(payload, topic);
                    break;
                case "patrol":
                    handlePatrolEvent(payload, topic);
                    break;
                case "acquisition":
                    handleAcquisitionEvent(payload, topic);
                    break;
                case "perception":
                    handlePerceptionEvent(payload, topic);
                    break;
                case "collection":
                    handleCollectionEvent(payload, topic);
                    break;
                default:
                    log.warn("未知的事件类型: {}, topic={}", eventType, topic);
            }

        } catch (Exception e) {
            log.error("处理MQTT消息失败, topic={}", topic, e);
        }
    }

    /**
     * 【已修改】只根据 deviceId + deviceTypeCode 查询，不判断 channel
     */
    private SasDevice findDevice(String deviceId, Integer deviceTypeCode) {
        return deviceService.getOne(new LambdaQueryWrapper<SasDevice>()
                .eq(SasDevice::getDeviceId, deviceId)
                .eq(SasDevice::getDeviceType, deviceTypeCode)
                .last("limit 1"));
    }

    /**
     * 兼容旧调用（自动忽略 channel）
     */
    private SasDevice findDevice(String deviceId, Integer channel, Integer deviceTypeCode) {
        return findDevice(deviceId, deviceTypeCode);
    }

    /**
     * 查询事件类型分组信息
     */
    private SasEventTypeGroup findEventTypeGroup(Integer deviceTypeCode) {
        return eventTypeGroupService.getOne(new LambdaQueryWrapper<SasEventTypeGroup>()
                .eq(SasEventTypeGroup::getDeviceType, deviceTypeCode)
                .last("limit 1"));
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleUsbEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                UsbEventMessage message = mapper.readValue(payload, UsbEventMessage.class);
                if(message.getEventCode() == null && root.get("code") != null) {
                    message.setEventCode(root.get("code").asInt());
                }

                SasDevice device = findDevice(message.getDeviceId(), SystemTypeCodeEnum.usb.getCode());
                if (device == null) {
                    log.warn("设备不存在，丢弃USB事件: deviceId={}", message.getDeviceId());
                    return;
                }

                SasUsbEvent event = new SasUsbEvent();
                String eventId = message.getId() != null && !message.getId().isEmpty()
                        ? message.getId()
                        : IdUtil.randomUUID();
                if (usbEventService.getById(eventId) != null) {
                    log.info("USB事件已存在, 跳过保存, eventId={}", eventId);
                    return;
                }
                event.setEventId(eventId);
                BeanUtil.copyProperties(message, event, "eventId");
                event.setCardId(message.getIcId());
                event.setTriggerTime(parseTime(message.getTriggerTime()));
                event.setCreateTime(LocalDateTime.now());
                event.setUpdateTime(LocalDateTime.now());

                if (message.getEventPic() != null) {
                    UsbEventMessage.PicInfo picInfo = message.getEventPic();
                    if (picInfo != null && StrUtil.isNotBlank(picInfo.getUrl()) && StrUtil.isNotBlank(picInfo.getPath())) {
                        SasPic pic = new SasPic();
                        pic.setId(IdUtil.randomUUID());
                        pic.setUrl(picInfo.getUrl());
                        pic.setPath(picInfo.getPath());
                        pic.setCreateTime(LocalDateTime.now());
                        pic.setUpdateTime(LocalDateTime.now());
                        sasPicMapper.insert(pic);
                        event.setPicId(pic.getId());
                    }
                }

                usbEventService.save(event);

                BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
                info.setTriggerTime(message.getTriggerTime());
                info.setCreateTime(LocalDateTime.now().format(formatter));

                if (event.getPicId() != null) {
                    SasPic pic = sasPicMapper.selectById(event.getPicId());
                    if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                        info.setEventUrl(pic.getUrl() + pic.getPath());
                    }
                }

                SasUsbEventCode code = usbEventCodeMapper.selectById(event.getEventCode());
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.usb.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.usb.getCode());
                SasWebSocket.sendAll(info);

                log.info("USB事件保存并推送成功, eventId={}", event.getEventId());
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.usb.getCode());
            }
        } catch (Exception e) {
            log.error("处理USB事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleSnapEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                SnapEventResult eventResult = (SnapEventResult)this.mapper.readValue(payload, SnapEventResult.class);
                log.info("实时智能分析");
                Map<String, Object> map = new HashMap();
                map.put("eventId", eventResult.getId());
                JsonRpcRequest getEventInfoJson = new JsonRpcRequest("getEvent", map, (Long)null);
                Map<String, Object> getEventInfo = new HashMap();
                getEventInfo.put("key", this.sasConfigService.getConfig().getKeyds());
                getEventInfo.put("json", getEventInfoJson.toString());
                log.info("请求AG报文：{}", JSONUtil.toJsonStr(getEventInfo));
                String resultEvent = HttpUtil.post(this.sasConfigService.getConfig().getHost() + "/agbox/device/snap", getEventInfo);
                log.info("请求AG响应：{}", resultEvent);
                SnapEventInfoVo eventInfoVo = (SnapEventInfoVo)this.mapper.readValue(resultEvent, SnapEventInfoVo.class);
                SnapEventInfoResult result = eventInfoVo.getResult();
                SnapEventInfo eventInfo = result.getEventInfo();
                SasSnapEvent bean = (SasSnapEvent)BeanUtil.toBean(eventInfo, SasSnapEvent.class);

                String eventId = eventResult.getId() != null && !eventResult.getId().isEmpty()
                        ? eventResult.getId()
                        : IdUtil.randomUUID();
                if (snapEventService.getById(eventId) != null) {
                    log.info("抓拍事件已存在, 跳过保存, eventId={}", eventId);
                    return;
                }
                bean.setEventId(eventId);

                BrieflyEventInfo info = (BrieflyEventInfo)BeanUtil.toBeanIgnoreError(bean, BrieflyEventInfo.class);
                if (eventInfo.getEventPic() != null) {
                    SasPic pic = (SasPic)BeanUtil.toBean(eventInfo.getEventPic(), SasPic.class);
                    pic.setId(IdUtil.randomUUID());
                    this.sasPicMapper.insert(pic);
                    bean.setEventPicId(pic.getId());
                    info.setEventUrl(pic.getUrl() + pic.getPath());
                }

                if (eventInfo.getScenePic() != null) {
                    SasPic pic = (SasPic)BeanUtil.toBean(eventInfo.getScenePic(), SasPic.class);
                    pic.setId(IdUtil.randomUUID());
                    this.sasPicMapper.insert(pic);
                    bean.setScenePicId(pic.getId());
                    info.setSceneUrl(pic.getUrl() + pic.getPath());
                }
                if(eventInfo.getEventCode() == null && root.get("code") != null) {
                    eventInfo.setEventCode(root.get("code").asInt());
                }

                SasDevice device = findDevice(eventInfo.getDeviceId(), SystemTypeCodeEnum.snap.getCode());
                if (device == null) {
                    log.warn("设备不存在，丢弃抓拍事件: deviceId={}", eventInfo.getDeviceId());
                    return;
                }
                BeanUtil.copyProperties(eventInfo, bean, "eventId");
                bean.setTriggerTime(parseTime(eventInfo.getTriggerTime()));
                bean.setCreateTime(LocalDateTime.now());
                bean.setUpdateTime(LocalDateTime.now());

                snapEventService.save(bean);

                info.setTriggerTime(eventInfo.getTriggerTime());
                info.setCreateTime(LocalDateTime.now().format(formatter));

                SasSnapTypeCode code = snapTypeCodeMapper.selectById(bean.getEventCode());
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.snap.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.snap.getCode());
                SasWebSocket.sendAll(info);

                log.info("抓拍事件保存并推送成功, eventId={}", bean.getEventId());
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.snap.getCode());
            }
        } catch (Exception e) {
            log.error("处理抓拍事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleEntranceEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                EntranceEventMessage message = mapper.readValue(payload, EntranceEventMessage.class);
                if(message.getEventCode() == null && root.get("code") != null) {
                    message.setEventCode(root.get("code").asInt());
                }

                // 【HTTP增强】尝试通过HTTP获取详细信息
                SasConfig config = sasConfigService.getConfig();
                if (config != null && StrUtil.isNotBlank(config.getHost()) && StrUtil.isNotBlank(config.getKeyds())) {
                    try {
                        Map<String, Object> map = new HashMap<>();
                        map.put("eventId", message.getId());
                        JsonRpcRequest getEntranceEventInfoJson = new JsonRpcRequest("getEvent", map, (Long)null);
                        Map<String, Object> getEntranceEventInfo = new HashMap<>();
                        getEntranceEventInfo.put("key", config.getKeyds());
                        getEntranceEventInfo.put("json", getEntranceEventInfoJson.toString());
                        log.info("【门禁】请求AG详细信息报文：{}", JSONUtil.toJsonStr(getEntranceEventInfo));

                        String resultEvent = HttpUtil.post(config.getHost() + "/agbox/device/entrance", getEntranceEventInfo);
                        log.info("【门禁】请求AG响应：{}", resultEvent);

                        if (StrUtil.isNotBlank(resultEvent)) {
                            EntranceEventInfoVo entranceEventInfoVo = mapper.readValue(resultEvent, EntranceEventInfoVo.class);
                            if (entranceEventInfoVo != null && entranceEventInfoVo.getResult() != null && entranceEventInfoVo.getResult().getEventInfo() != null) {
                                // HTTP获取成功，使用详细信息处理
                                EntranceEventInfo eventInfo = entranceEventInfoVo.getResult().getEventInfo();
                                processEntranceEventWithHttpInfo(eventInfo, message);
                                return;
                            }
                        }
                    } catch (Exception httpEx) {
                        log.warn("【门禁】HTTP获取详细信息失败，降级到MQTT处理：{}", httpEx.getMessage());
                        // 降级到MQTT处理
                    }
                }
                
                // MQTT基础处理（降级方案）
                log.info("【门禁】使用MQTT基础信息处理");
                processEntranceEventWithMqttInfo(message);
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.entrance.getCode());
            }
        } catch (Exception e) {
            log.error("处理门禁事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleParkingEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                ParkingEventResult eventResult = this.mapper.readValue(payload, ParkingEventResult.class);
                
                // 【HTTP增强】尝试通过HTTP获取详细信息
                SasConfig config = sasConfigService.getConfig();
                if (config != null && StrUtil.isNotBlank(config.getHost()) && StrUtil.isNotBlank(config.getKeyds())) {
                    try {
                        Map<String, Object> map = new HashMap<>();
                        map.put("eventId", eventResult.getId());
                        JsonRpcRequest getParkingEventInfoJson = new JsonRpcRequest("getEvent", map, (Long)null);
                        Map<String, Object> getParkingEventInfo = new HashMap<>();
                        getParkingEventInfo.put("key", config.getKeyds());
                        getParkingEventInfo.put("json", getParkingEventInfoJson.toString());
                        log.info("【停车场】请求AG详细信息报文：{}", JSONUtil.toJsonStr(getParkingEventInfo));

                        String resultEvent = HttpUtil.post(config.getHost() + "/agbox/device/parking", getParkingEventInfo);
                        log.info("【停车场】请求AG响应：{}", resultEvent);

                        if (StrUtil.isNotBlank(resultEvent)) {
                            ParkingEventInfoVo parkingEventInfoVo = this.mapper.readValue(resultEvent, ParkingEventInfoVo.class);
                            if (parkingEventInfoVo != null && parkingEventInfoVo.getResult() != null && parkingEventInfoVo.getResult().getEventInfo() != null) {
                                // HTTP获取成功，使用详细信息处理
                                ParkingEventInfo eventInfo = parkingEventInfoVo.getResult().getEventInfo();
                                processParkingEventWithHttpInfo(eventInfo, eventResult);
                                return;
                            }
                        }
                    } catch (Exception httpEx) {
                        log.warn("【停车场】HTTP获取详细信息失败，降级到MQTT处理：{}", httpEx.getMessage());
                        // 降级到MQTT处理
                    }
                }
                
                // MQTT基础处理（降级方案）
                log.info("【停车场】使用MQTT基础信息处理");
                processParkingEventWithMqttInfo(eventResult, root);
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.parking.getCode());
            }
        } catch (Exception e) {
            log.error("处理停车场事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleRoadblockEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                RoadblockEventMessage message = mapper.readValue(payload, RoadblockEventMessage.class);
                if(message.getEventCode() == null && root.get("code") != null) {
                    message.setEventCode(root.get("code").asInt());
                }

                SasDevice device = findDevice(message.getDeviceId(), SystemTypeCodeEnum.roadblock.getCode());
                if (device == null) {
                    log.warn("设备不存在，丢弃阻车路障事件: deviceId={}", message.getDeviceId());
                    return;
                }

                SasRoadblockEvent event = new SasRoadblockEvent();
                String eventId = message.getId() != null && !message.getId().isEmpty()
                        ? message.getId()
                        : IdUtil.randomUUID();
                if (roadblockEventService.getById(eventId) != null) {
                    log.info("阻车路障事件已存在, 跳过保存, eventId={}", eventId);
                    return;
                }
                event.setEventId(eventId);
                BeanUtil.copyProperties(message, event, "eventId");
                event.setTriggerTime(parseTime(message.getTriggerTime()));
                event.setCreateTime(LocalDateTime.now());
                event.setUpdateTime(LocalDateTime.now());

                if (message.getRoadblockPic() != null) {
                    RoadblockEventMessage.PicInfo picInfo = message.getRoadblockPic();
                    if (picInfo != null && StrUtil.isNotBlank(picInfo.getUrl()) && StrUtil.isNotBlank(picInfo.getPath())) {
                        SasPic pic = new SasPic();
                        pic.setId(IdUtil.randomUUID());
                        pic.setUrl(picInfo.getUrl());
                        pic.setPath(picInfo.getPath());
                        pic.setCreateTime(LocalDateTime.now());
                        pic.setUpdateTime(LocalDateTime.now());
                        sasPicMapper.insert(pic);
                        event.setPicId(pic.getId());
                    }
                }

                roadblockEventService.save(event);

                BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
                info.setTriggerTime(message.getTriggerTime());
                info.setCreateTime(LocalDateTime.now().format(formatter));

                if (event.getPicId() != null) {
                    SasPic pic = sasPicMapper.selectById(event.getPicId());
                    if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                        info.setEventUrl(pic.getUrl() + pic.getPath());
                    }
                }

                SasRoadblockEventCode code = roadblockEventCodeMapper.selectById(event.getEventCode());
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.roadblock.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.roadblock.getCode());
                SasWebSocket.sendAll(info);

                log.info("阻车路障事件保存并推送成功, eventId={}", event.getEventId());
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.roadblock.getCode());
            }
        } catch (Exception e) {
            log.error("处理阻车路障事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleAlarsasEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            // 【核心修复】匹配真实设备的 method: "event"
            if ("event".equals(method)) {
                // 从MQTT获取基础信息
                String id = root.path("id").asText();
                String deviceId = root.path("deviceId").asText();
                
                // 打印日志，确认字段都读到了
                log.info("【入侵告警】收到MQTT消息：deviceId={}, id={}", deviceId, id);

                // 【HTTP增强】尝试通过HTTP获取详细信息
                SasConfig config = sasConfigService.getConfig();
                if (config != null && StrUtil.isNotBlank(config.getHost()) && StrUtil.isNotBlank(config.getKeyds())) {
                    try {
                        Map<String, Object> map = new HashMap<>();
                        map.put("eventId", id);
                        JsonRpcRequest getAlarmEventInfoJson = new JsonRpcRequest("getEvent", map, (Long)null);
                        Map<String, Object> getAlarmEventInfo = new HashMap<>();
                        getAlarmEventInfo.put("key", config.getKeyds());
                        getAlarmEventInfo.put("json", getAlarmEventInfoJson.toString());
                        log.info("【入侵告警】请求AG详细信息报文：{}", JSONUtil.toJsonStr(getAlarmEventInfo));

                        String resultEvent = HttpUtil.post(config.getHost() + "/agbox/device/alarm", getAlarmEventInfo);
                        log.info("【入侵告警】请求AG响应：{}", resultEvent);

                        if (StrUtil.isNotBlank(resultEvent)) {
                            AlarmEventInfoVo eventInfoVo = mapper.readValue(resultEvent, AlarmEventInfoVo.class);
                            if (eventInfoVo != null && eventInfoVo.getResult() != null && eventInfoVo.getResult().getEventInfo() != null) {
                                // HTTP获取成功，使用详细信息处理
                                AlarmEventInfo eventInfo = eventInfoVo.getResult().getEventInfo();
                                processAlarmEventWithHttpInfo(eventInfo, root, id);
                                return;
                            }
                        }
                    } catch (Exception httpEx) {
                        log.warn("【入侵告警】HTTP获取详细信息失败，降级到MQTT处理：{}", httpEx.getMessage());
                        // 降级到MQTT处理
                    }
                }
                
                // MQTT基础处理（降级方案）
                log.info("【入侵告警】使用MQTT基础信息处理");
                Integer channel = root.path("channel").asInt();
                Integer eventCode = root.path("code").asInt();
                String triggerTime = root.path("triggerTime").asText(); 
                String note = root.path("note").asText();
                Integer eventSystem = root.path("eventSystem").asInt();

                // 设备校验
                SasDevice device = findDevice(deviceId, SystemTypeCodeEnum.alarm.getCode());
                if (device == null) {
                    log.warn("【入侵告警】设备不存在，丢弃消息：deviceId={}", deviceId);
                    return;
                }

                // 构建事件实体
                SasAlarsasEvent event = new SasAlarsasEvent();
                String eventId = StrUtil.isNotBlank(id) ? id : IdUtil.randomUUID();

                // 防重复
                if (alarsasEventService.getById(eventId) != null) {
                    log.info("【入侵告警】事件已存在，跳过保存：eventId={}", eventId);
                    return;
                }

                // 手动赋值，100% 不会丢字段
                event.setEventId(eventId);
                event.setDeviceId(deviceId);
                event.setChannel(channel);
                event.setEventCode(eventCode);
                event.setNote(note);
                event.setEventSystem(eventSystem);
                event.setTriggerTime(parseTime(triggerTime));
                event.setCreateTime(LocalDateTime.now());
                event.setUpdateTime(LocalDateTime.now());

                // 【新增】处理图片信息
                if (root.path("alarmPic") != null && !root.path("alarmPic").isMissingNode()) {
                    JsonNode picNode = root.path("alarmPic");
                    String picUrl = picNode.path("url").asText();
                    String picPath = picNode.path("path").asText();
                    
                    if (StrUtil.isNotBlank(picUrl) && StrUtil.isNotBlank(picPath)) {
                        SasPic pic = new SasPic();
                        pic.setId(IdUtil.randomUUID());
                        pic.setUrl(picUrl);
                        pic.setPath(picPath);
                        pic.setCreateTime(LocalDateTime.now());
                        pic.setUpdateTime(LocalDateTime.now());
                        sasPicMapper.insert(pic);
                        event.setPicId(pic.getId());
                        log.debug("【入侵告警】图片保存成功，picId={}", pic.getId());
                    }
                }

                // 保存到数据库
                alarsasEventService.save(event);
                log.info("【入侵告警】保存成功！eventId={}, deviceId={}, eventCode={}",
                        eventId, deviceId, eventCode);

                // 后续的 WebSocket 推送逻辑（保持不变）
                BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
                info.setTriggerTime(triggerTime);
                info.setCreateTime(LocalDateTime.now().format(formatter));

                // 【新增】设置图片URL（如果有）
                if (event.getPicId() != null) {
                    SasPic pic = sasPicMapper.selectById(event.getPicId());
                    if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                        info.setEventUrl(pic.getUrl() + pic.getPath());
                        log.debug("【入侵告警】图片URL设置成功：{}", info.getEventUrl());
                    }
                }

                SasAlarsasEventCode code = alarsasEventCodeMapper.selectById(eventCode);
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.alarm.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.alarm.getCode());
                SasWebSocket.sendAll(info);

            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.alarm.getCode());
            }
        } catch (Exception e) {
            log.error("【入侵告警】处理失败，payload={}", payload, e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handlePatrolEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                PatrolEventResult eventResult = (PatrolEventResult)this.mapper.readValue(payload, PatrolEventResult.class);
                Map<String, Object> map = new HashMap();
                map.put("eventId", eventResult.getId());
                JsonRpcRequest getPatrolEventInfoJson = new JsonRpcRequest("getEvent", map, (Long)null);
                Map<String, Object> getPatrolEventInfo = new HashMap();
                getPatrolEventInfo.put("key", this.sasConfigService.getConfig().getKeyds());
                getPatrolEventInfo.put("json", getPatrolEventInfoJson.toString());
                log.info("请求AG报文：{}", JSONUtil.toJsonStr(getPatrolEventInfo));
                String resultEvent = HttpUtil.post(this.sasConfigService.getConfig().getHost() + "/agbox/device/patrol", getPatrolEventInfo);
                log.info("请求AG响应：{}", resultEvent);
                PatrolEventInfoVo patrolEventInfoVo = (PatrolEventInfoVo)this.mapper.readValue(resultEvent, PatrolEventInfoVo.class);
                PatrolEventInfoResult result = patrolEventInfoVo.getResult();
                PatrolEventInfo eventInfo = result.getEventInfo();

                SasDevice device = findDevice(eventInfo.getDeviceId(), SystemTypeCodeEnum.patrol.getCode());
                if (device == null) {
                    log.warn("设备不存在，丢弃巡检事件: deviceId={}", eventInfo.getDeviceId());
                    return;
                }

                SasPatrolEvent event = new SasPatrolEvent();
                String eventId = patrolEventInfoVo.getId() != null && !patrolEventInfoVo.getId().isEmpty()
                        ? patrolEventInfoVo.getId()
                        : IdUtil.randomUUID();
                if (patrolEventService.getById(eventId) != null) {
                    log.info("巡检事件已存在, 跳过保存, eventId={}", eventId);
                    return;
                }
                event.setEventId(eventId);
                BeanUtil.copyProperties(eventInfo, event, "eventId");
                String triggerTimeStr = eventInfo.getTriggerTime().replaceAll("\\s", " ");
                event.setTriggerTime(LocalDateTime.parse(triggerTimeStr, formatter));
                event.setCreateTime(LocalDateTime.now());
                event.setUpdateTime(LocalDateTime.now());

                if (eventInfo.getEventPic() != null) {
                    SasPic pic = (SasPic) BeanUtil.toBean(eventInfo.getEventPic(), SasPic.class);
                    pic.setId(IdUtil.randomUUID());
                    this.sasPicMapper.insert(pic);
                    event.setPicId(pic.getId());
                }

                if (eventInfo.getUserParams() != null) {
                    SasPatrolUserParam userParam = (SasPatrolUserParam)BeanUtil.toBean(eventInfo.getUserParams(), SasPatrolUserParam.class);
                    userParam.setId(IdUtil.randomUUID());
                    LocalDateTime now = LocalDateTime.now();
                    userParam.setCreateTime(now);
                    userParam.setUpdateTime(now);
                    this.sasPatrolUserParamService.save(userParam);
                    event.setUserParasasId(userParam.getId());
                }

                patrolEventService.save(event);

                BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
                info.setTriggerTime(eventInfo.getTriggerTime());
                info.setCreateTime(LocalDateTime.now().format(formatter));

                if (event.getPicId() != null) {
                    SasPic pic = sasPicMapper.selectById(event.getPicId());
                    if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                        info.setEventUrl(pic.getUrl() + pic.getPath());
                    }
                }

                SasPatrolEventCode code = patrolEventCodeMapper.selectById(event.getEventCode());
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.patrol.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.patrol.getCode());
                SasWebSocket.sendAll(info);

                log.info("巡检事件保存并推送成功, eventId={}", event.getEventId());
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.patrol.getCode());
            }
        } catch (Exception e) {
            log.error("处理巡检事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleAcquisitionEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                AcquisitionEventMessage message = mapper.readValue(payload, AcquisitionEventMessage.class);
                if(message.getEventCode() == null && root.get("code") != null) {
                    message.setEventCode(root.get("code").asInt());
                }

                SasDevice device = findDevice(message.getDeviceId(), SystemTypeCodeEnum.acquisition.getCode());
                if (device == null) {
                    log.warn("设备不存在，丢弃数据采集事件: deviceId={}", message.getDeviceId());
                    return;
                }

                SasAcquisitionEvent event = new SasAcquisitionEvent();
                String eventId = message.getId() != null && !message.getId().isEmpty()
                        ? message.getId()
                        : IdUtil.randomUUID();
                if (acquisitionEventService.getById(eventId) != null) {
                    log.info("数据采集事件已存在, 跳过保存, eventId={}", eventId);
                    return;
                }
                event.setEventId(eventId);
                BeanUtil.copyProperties(message, event, "eventId");
                event.setTriggerTime(parseTime(message.getTriggerTime()));
                event.setCreateTime(LocalDateTime.now());
                event.setUpdateTime(LocalDateTime.now());

                if (message.getAcquisitionPic() != null) {
                    AcquisitionEventMessage.PicInfo picInfo = message.getAcquisitionPic();
                    if (picInfo != null && StrUtil.isNotBlank(picInfo.getUrl()) && StrUtil.isNotBlank(picInfo.getPath())) {
                        SasPic pic = new SasPic();
                        pic.setId(IdUtil.randomUUID());
                        pic.setUrl(picInfo.getUrl());
                        pic.setPath(picInfo.getPath());
                        pic.setCreateTime(LocalDateTime.now());
                        pic.setUpdateTime(LocalDateTime.now());
                        sasPicMapper.insert(pic);
                        event.setPicId(pic.getId());
                    }
                }

                acquisitionEventService.save(event);

                BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
                info.setTriggerTime(message.getTriggerTime());
                info.setCreateTime(LocalDateTime.now().format(formatter));

                if (event.getPicId() != null) {
                    SasPic pic = sasPicMapper.selectById(event.getPicId());
                    if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                        info.setEventUrl(pic.getUrl() + pic.getPath());
                    }
                }

                SasAcquisitionEventCode code = acquisitionEventCodeMapper.selectById(event.getEventCode());
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.acquisition.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.acquisition.getCode());
                SasWebSocket.sendAll(info);

                log.info("数据采集事件保存并推送成功, eventId={}", event.getEventId());
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.acquisition.getCode());
            }
        } catch (Exception e) {
            log.error("处理数据采集事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handlePerceptionEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                PerceptionEventMessage message = mapper.readValue(payload, PerceptionEventMessage.class);
                if(message.getEventCode() == null && root.get("code") != null) {
                    message.setEventCode(root.get("code").asInt());
                }

                SasDevice device = findDevice(message.getDeviceId(), SystemTypeCodeEnum.perception.getCode());
                if (device == null) {
                    log.warn("设备不存在，丢弃状态感知事件: deviceId={}", message.getDeviceId());
                    return;
                }

                SasPerceptionEvent event = new SasPerceptionEvent();
                String eventId = message.getId() != null && !message.getId().isEmpty()
                        ? message.getId()
                        : IdUtil.randomUUID();
                if (perceptionEventService.getById(eventId) != null) {
                    log.info("状态感知事件已存在, 跳过保存, eventId={}", eventId);
                    return;
                }
                event.setEventId(eventId);
                BeanUtil.copyProperties(message, event, "eventId");
                event.setTriggerTime(parseTime(message.getTriggerTime()));
                event.setCreateTime(LocalDateTime.now());
                event.setUpdateTime(LocalDateTime.now());

                log.info("检查图片信息 - perceptionPic: {}", message.getPerceptionPic());
                if (message.getPerceptionPic() != null) {
                    PerceptionEventMessage.PicInfo picInfo = message.getPerceptionPic();
                    log.info("图片详情 - URL: {}, Path: {}", picInfo.getUrl(), picInfo.getPath());
                    if (picInfo != null && StrUtil.isNotBlank(picInfo.getUrl()) && StrUtil.isNotBlank(picInfo.getPath())) {
                        log.info("开始保存图片到数据库");
                        SasPic pic = new SasPic();
                        pic.setId(IdUtil.randomUUID());
                        pic.setUrl(picInfo.getUrl());
                        pic.setPath(picInfo.getPath());
                        pic.setCreateTime(LocalDateTime.now());
                        pic.setUpdateTime(LocalDateTime.now());
                        int insert = sasPicMapper.insert(pic);
                        log.info("图片插入结果: {}, 图片ID: {}", insert, pic.getId());
                        event.setPicId(pic.getId());
                    } else {
                        log.info("图片信息为空或不完整，跳过保存");
                    }
                } else {
                    log.info("perceptionPic字段为null");
                }

                perceptionEventService.save(event);
                log.info("状态感知事件保存完成 - EventId: {}, PicId: {}", event.getEventId(), event.getPicId());

                BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
                info.setTriggerTime(message.getTriggerTime());
                info.setCreateTime(LocalDateTime.now().format(formatter));

                if (event.getPicId() != null) {
                    SasPic pic = sasPicMapper.selectById(event.getPicId());
                    if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                        info.setEventUrl(pic.getUrl() + pic.getPath());
                    }
                }

                SasPerceptionEventCode code = perceptionEventCodeMapper.selectById(event.getEventCode());
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.perception.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.perception.getCode());
                SasWebSocket.sendAll(info);

                log.info("状态感知事件保存并推送成功, eventId={}", event.getEventId());
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.perception.getCode());
            }
        } catch (Exception e) {
            log.error("处理状态感知事件失败", e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleCollectionEvent(String payload, String topic) {
        try {
            JsonNode root = mapper.readTree(payload);
            String method = root.path("method").asText();

            if ("event".equals(method)) {
                CollectionEventMessage message = mapper.readValue(payload, CollectionEventMessage.class);
                if(message.getEventCode() == null && root.get("code") != null) {
                    message.setEventCode(root.get("code").asInt());
                }

                SasDevice device = findDevice(message.getDeviceId(), SystemTypeCodeEnum.collection.getCode());
                if (device == null) {
                    log.warn("设备不存在，丢弃状态采集事件: deviceId={}", message.getDeviceId());
                    return;
                }

                SasCollectionEvent event = new SasCollectionEvent();
                String eventId = message.getId() != null && !message.getId().isEmpty()
                        ? message.getId()
                        : IdUtil.randomUUID();
                if (collectionEventService.getById(eventId) != null) {
                    log.info("状态采集事件已存在, 跳过保存, eventId={}", eventId);
                    return;
                }
                event.setEventId(eventId);
                BeanUtil.copyProperties(message, event, "eventId");
                event.setTriggerTime(parseTime(message.getTriggerTime()));
                event.setCreateTime(LocalDateTime.now());
                event.setUpdateTime(LocalDateTime.now());

                if (message.getScenePicInfo() != null) {
                    SasPic bean = (SasPic)BeanUtil.toBean(message.getScenePicInfo(), SasPic.class);
                    bean.setId(IdUtil.randomUUID());
                    this.sasPicMapper.insert(bean);
                    event.setScenePicId(bean.getId());
                }

                if (message.getEventPicInfo() != null) {
                    SasPic bean = (SasPic)BeanUtil.toBean(message.getEventPicInfo(), SasPic.class);
                    bean.setId(IdUtil.randomUUID());
                    this.sasPicMapper.insert(bean);
                    event.setPicId(bean.getId());
                }

                collectionEventService.save(event);

                BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
                info.setTriggerTime(message.getTriggerTime());
                info.setCreateTime(LocalDateTime.now().format(formatter));

                // 【修复】从数据库中获取图片URL，确保一致性
                if (event.getPicId() != null) {
                    SasPic eventPic = sasPicMapper.selectById(event.getPicId());
                    if (eventPic != null && StrUtil.isNotBlank(eventPic.getUrl()) && StrUtil.isNotBlank(eventPic.getPath())) {
                        info.setEventUrl(eventPic.getUrl() + eventPic.getPath());
                    }
                }
                
                if (event.getScenePicId() != null) {
                    SasPic scenePic = sasPicMapper.selectById(event.getScenePicId());
                    if (scenePic != null && StrUtil.isNotBlank(scenePic.getUrl()) && StrUtil.isNotBlank(scenePic.getPath())) {
                        // 如果还没有事件URL，使用场景图片
                        if (info.getEventUrl() == null) {
                            info.setEventUrl(scenePic.getUrl() + scenePic.getPath());
                        }
                        info.setSceneUrl(scenePic.getUrl() + scenePic.getPath());
                    }
                }

                SasCollectionEventCode code = collectionEventCodeMapper.selectById(event.getEventCode());
                if (code != null) {
                    info.setEventTypeName(code.getName());
                }

                enrichDeviceInfo(info, device);
                SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.collection.getCode());
                if (eventTypeGroup != null) {
                    info.setEventLevelId(eventTypeGroup.getEventLevel());
                }
                info.setDeviceType(SystemTypeCodeEnum.collection.getCode());
                SasWebSocket.sendAll(info);

                log.info("状态采集事件保存并推送成功, eventId={}", event.getEventId());
            } else if ("heart".equals(method)) {
                handleHeartbeat(payload, SystemTypeCodeEnum.collection.getCode());
            }
        } catch (Exception e) {
            log.error("处理状态采集事件失败", e);
        }
    }

    private void handleHeartbeat(String payload, int deviceTypeCode) {
        try {
            GlobalDeviceHeartInfo heartInfo = mapper.readValue(payload, GlobalDeviceHeartInfo.class);
            List<SasDevice> devices = deviceService.list(new LambdaQueryWrapper<SasDevice>()
                    .eq(SasDevice::getDeviceId, heartInfo.getDeviceId())
                    .eq(SasDevice::getDeviceType, deviceTypeCode));

            if (CollUtil.isNotEmpty(devices)) {
                GisHeartInfo heartInfoGis = heartInfo.getGis();
                List<SasDevice> updateList = devices.stream().map(device -> {
                    device.setTriggerTime(LocalDateTime.now());
                    device.setUpdateTime(LocalDateTime.now());

                    if (heartInfoGis != null && StrUtil.isNotBlank(device.getGisId())) {
                        SasGis gis = gisMapper.selectById(device.getGisId());
                        if (gis != null) {
                            if (heartInfoGis.getLongitude() != null) gis.setLon(heartInfoGis.getLongitude());
                            if (heartInfoGis.getLatitude() != null) gis.setLat(heartInfoGis.getLatitude());
                            if (heartInfoGis.getAltitude() != null) gis.setAlt(heartInfoGis.getAltitude());
                            gisMapper.updateById(gis);
                        }
                    }
                    return device;
                }).collect(Collectors.toList());

                deviceService.updateBatchById(updateList);
                log.debug("更新设备心跳成功, deviceId={}, count={}", heartInfo.getDeviceId(), updateList.size());
            }
        } catch (Exception e) {
            log.error("处理设备心跳失败", e);
        }
    }

    private void enrichDeviceInfo(BrieflyEventInfo info, SasDevice device) {
        if (device != null) {
            info.setAddress(device.getAddress());
            if (StrUtil.isBlank(info.getNote())) {
                info.setNote(device.getNote());
            }
        }
    }

    private void enrichDeviceInfo(BrieflyEventInfo info, String deviceId, Integer channel, Integer deviceTypeCode) {
        SasDevice device = findDevice(deviceId, deviceTypeCode);
        enrichDeviceInfo(info, device);
    }

    private String extractDeviceId(String topic) {
        String[] parts = topic.split("/");
        return parts.length > 0 ? parts[parts.length - 1] : "unknown";
    }

    private LocalDateTime parseTime(String timeStr) {
        if (timeStr == null || timeStr.isEmpty()) {
            return LocalDateTime.now();
        }
        try {
            return LocalDateTime.parse(timeStr, formatter);
        } catch (Exception e) {
            log.warn("时间解析失败: {}, 使用当前时间", timeStr);
            return LocalDateTime.now();
        }
    }

    public void subscribe(String topic) {
        try {
            if (client != null && client.isConnected()) {
                client.subscribe(topic);
                log.info("订阅主题成功: {}", topic);
            }
        } catch (MqttException e) {
            log.error("订阅主题失败: {}", topic, e);
        }
    }

    public void pauseListening() {
        this.isListening = false;
        log.info("MQTT监听已暂停");
    }

    public void resumeListening() {
        this.isListening = true;
        log.info("MQTT监听已恢复");
    }

    public boolean getConnectionStatus() {
        return client != null && client.isConnected();
    }

    public boolean getListeningStatus() {
        return isListening;
    }

    public void reconnect() {
        try {
            if (client != null) {
                if (client.isConnected()) {
                    client.disconnect();
                }
                client.close();
                client = null;
            }
            isListening = true;
            init();
        } catch (Exception e) {
            log.error("MQTT重连失败", e);
            isListening = false;
        }
    }

    /**
     * 使用HTTP获取的详细信息处理入侵告警事件
     */
    private void processAlarmEventWithHttpInfo(AlarmEventInfo eventInfo, JsonNode mqttRoot, String id) {
        try {
            SasDevice device = findDevice(eventInfo.getDeviceId(), SystemTypeCodeEnum.alarm.getCode());
            if (device == null) {
                log.warn("【入侵告警-HTTP】设备不存在，丢弃事件：deviceId={}", eventInfo.getDeviceId());
                return;
            }

            String eventId = StrUtil.isNotBlank(id) ? id : IdUtil.randomUUID();
            if (alarsasEventService.getById(eventId) != null) {
                log.info("【入侵告警-HTTP】事件已存在，跳过保存：eventId={}", eventId);
                return;
            }

            SasAlarsasEvent event = new SasAlarsasEvent();
            event.setEventId(eventId);
            
            // 从HTTP响应中获取详细信息
            BeanUtil.copyProperties(eventInfo, event);
            event.setEventSystem(eventInfo.getSystemCode());
            event.setTriggerTime(parseTime(eventInfo.getTriggerTime()));
            event.setCreateTime(LocalDateTime.now());
            event.setUpdateTime(LocalDateTime.now());

            // 处理HTTP中的图片信息
            if (eventInfo.getEventPic() != null) {
                SasPic pic = BeanUtil.toBean(eventInfo.getEventPic(), SasPic.class);
                pic.setId(IdUtil.randomUUID());
                pic.setCreateTime(LocalDateTime.now());
                pic.setUpdateTime(LocalDateTime.now());
                sasPicMapper.insert(pic);
                event.setPicId(pic.getId());
                log.debug("【入侵告警-HTTP】图片保存成功，picId={}", pic.getId());
            }

            // 保存到数据库
            alarsasEventService.save(event);
            log.info("【入侵告警-HTTP】保存成功！eventId={}, deviceId={}, eventCode={}",
                    eventId, eventInfo.getDeviceId(), eventInfo.getEventCode());

            // WebSocket推送
            BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
            info.setTriggerTime(eventInfo.getTriggerTime());
            info.setCreateTime(LocalDateTime.now().format(formatter));

            // 设置图片URL
            if (event.getPicId() != null) {
                SasPic pic = sasPicMapper.selectById(event.getPicId());
                if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                    info.setEventUrl(pic.getUrl() + pic.getPath());
                    log.debug("【入侵告警-HTTP】图片URL设置成功：{}", info.getEventUrl());
                }
            }

            SasAlarsasEventCode code = alarsasEventCodeMapper.selectById(eventInfo.getEventCode());
            if (code != null) {
                info.setEventTypeName(code.getName());
            }

            enrichDeviceInfo(info, device);
            SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.alarm.getCode());
            if (eventTypeGroup != null) {
                info.setEventLevelId(eventTypeGroup.getEventLevel());
            }
            info.setDeviceType(SystemTypeCodeEnum.alarm.getCode());
            SasWebSocket.sendAll(info);

        } catch (Exception e) {
            log.error("【入侵告警-HTTP】处理失败", e);
        }
    }

    @PreDestroy
    public void disconnect() {
        try {
            if (client != null) {
                if (client.isConnected()) {
                    client.disconnect();
                    log.info("MQTT连接已断开");
                }
                client.close();
                client = null;
            }
        } catch (MqttException e) {
            log.error("断开MQTT连接失败", e);
        }
    }

    /**
     * 使用HTTP获取的详细信息处理车辆抓拍事件
     */
    private void processParkingEventWithHttpInfo(ParkingEventInfo eventInfo, ParkingEventResult eventResult) {
        try {
            SasDevice device = findDevice(eventInfo.getDeviceId(), SystemTypeCodeEnum.parking.getCode());
            if (device == null) {
                log.warn("【车辆抓拍-HTTP】设备不存在，丢弃事件：deviceId={}", eventInfo.getDeviceId());
                return;
            }

            String eventId = StrUtil.isNotBlank(eventResult.getId()) ? eventResult.getId() : IdUtil.randomUUID();
            if (parkingEventService.getById(eventId) != null) {
                log.info("【车辆抓拍-HTTP】事件已存在，跳过保存：eventId={}", eventId);
                return;
            }

            // 处理图片信息
            String eventPicId = null;
            String platePicId = null;
            
            if (eventInfo.getEventPic() != null) {
                try {
                    SasPic eventPic = BeanUtil.toBean(eventInfo.getEventPic(), SasPic.class);
                    eventPic.setId(IdUtil.randomUUID());
                    eventPic.setCreateTime(LocalDateTime.now());
                    eventPic.setUpdateTime(LocalDateTime.now());
                    sasPicMapper.insert(eventPic);
                    eventPicId = eventPic.getId();
                    log.info("【车辆抓拍-HTTP】事件图片保存成功，eventPicId={}", eventPicId);
                } catch (Exception e) {
                    log.error("【车辆抓拍-HTTP】事件图片保存失败", e);
                    throw e;
                }
            }

            if (eventInfo.getPlacePic() != null) {
                try {
                    SasPic platePic = BeanUtil.toBean(eventInfo.getPlacePic(), SasPic.class);
                    platePic.setId(IdUtil.randomUUID());
                    platePic.setCreateTime(LocalDateTime.now());
                    platePic.setUpdateTime(LocalDateTime.now());
                    sasPicMapper.insert(platePic);
                    platePicId = platePic.getId();
                    log.info("【车辆抓拍-HTTP】车牌图片保存成功，platePicId={}", platePicId);
                } catch (Exception e) {
                    log.error("【车辆抓拍-HTTP】车牌图片保存失败", e);
                    throw e;
                }
            }

            SasParkingEvent event = new SasParkingEvent();
            event.setEventId(eventId);
            // 排除可能产生冲突的字段，避免被覆盖
            BeanUtil.copyProperties(eventInfo, event, "eventPicId", "platePicId", "createTime", "updateTime");
            event.setEventPicId(eventPicId);
            event.setPlatePicId(platePicId);
            event.setTriggerTime(parseTime(eventInfo.getTriggerTime()));
            // event.setReceivingTime(parseTime(eventInfo.getReceivingTime())); // 不存在此字段
            event.setCreateTime(LocalDateTime.now());
            event.setUpdateTime(LocalDateTime.now());

            // 确保关键字段不被覆盖
            event.setAccessType(eventInfo.getIo());
            event.setCarType(eventInfo.getCarCode());
            event.setPlateType(eventInfo.getPlateCode());

            parkingEventService.save(event);
            log.info("【车辆抓拍-HTTP】保存成功！eventId={}, deviceId={}, eventCode={}",
                    eventId, eventInfo.getDeviceId(), eventInfo.getEventCode());

            // WebSocket推送
            BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
            info.setTriggerTime(eventInfo.getTriggerTime());
            info.setCreateTime(LocalDateTime.now().format(formatter));

            // 设置图片URL
            if (event.getEventPicId() != null) {
                SasPic eventPic = sasPicMapper.selectById(event.getEventPicId());
                if (eventPic != null && StrUtil.isNotBlank(eventPic.getUrl()) && StrUtil.isNotBlank(eventPic.getPath())) {
                    info.setEventUrl(eventPic.getUrl() + eventPic.getPath());
                    log.debug("【车辆抓拍-HTTP】事件图片URL设置成功：{}", info.getEventUrl());
                }
            }
            if (event.getPlatePicId() != null) {
                SasPic platePic = sasPicMapper.selectById(event.getPlatePicId());
                if (platePic != null && StrUtil.isNotBlank(platePic.getUrl()) && StrUtil.isNotBlank(platePic.getPath())) {
                    if (info.getEventUrl() == null) {
                        info.setEventUrl(platePic.getUrl() + platePic.getPath());
                    }
                    info.setSceneUrl(platePic.getUrl() + platePic.getPath());
                    log.debug("【车辆抓拍-HTTP】车牌图片URL设置成功：{}", info.getSceneUrl());
                }
            }

            SasParkingEventCode code = parkingEventCodeMapper.selectById(event.getEventCode());
            if (code != null) {
                info.setEventTypeName(code.getName());
            }

            enrichDeviceInfo(info, device);
            SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.parking.getCode());
            if (eventTypeGroup != null) {
                info.setEventLevelId(eventTypeGroup.getEventLevel());
            }
            info.setDeviceType(SystemTypeCodeEnum.parking.getCode());
            SasWebSocket.sendAll(info);

        } catch (Exception e) {
            log.error("【车辆抓拍-HTTP】处理失败", e);
        }
    }

    /**
     * 使用MQTT基础信息处理车辆抓拍事件（降级方案）
     */
    private void processParkingEventWithMqttInfo(ParkingEventResult eventResult, JsonNode mqttRoot) {
        try {
            // 从MQTT消息中获取设备信息
            String deviceId = mqttRoot.path("deviceId").asText();
            if (StrUtil.isBlank(deviceId)) {
                log.warn("【车辆抓拍-MQTT】MQTT消息中没有deviceId，丢弃事件");
                return;
            }
            
            SasDevice device = findDevice(deviceId, SystemTypeCodeEnum.parking.getCode());
            if (device == null) {
                log.warn("【车辆抓拍-MQTT】设备不存在，丢弃事件：deviceId={}", deviceId);
                return;
            }

            String eventId = StrUtil.isNotBlank(eventResult.getId()) ? eventResult.getId() : IdUtil.randomUUID();
            if (parkingEventService.getById(eventId) != null) {
                log.info("【车辆抓拍-MQTT】事件已存在，跳过保存：eventId={}", eventId);
                return;
            }

            SasParkingEvent event = new SasParkingEvent();
            event.setEventId(eventId);
            event.setDeviceId(deviceId);
            event.setEventCode(mqttRoot.path("code").asInt());
            event.setTriggerTime(parseTime(mqttRoot.path("triggerTime").asText()));
            event.setCreateTime(LocalDateTime.now());
            event.setUpdateTime(LocalDateTime.now());

            // MQTT基础信息不包含详细图片，需要设置为null
            event.setEventPicId(null);
            event.setPlatePicId(null);

            parkingEventService.save(event);
            log.info("【车辆抓拍-MQTT】保存成功！eventId={}, deviceId={}, eventCode={}",
                    eventId, deviceId, event.getEventCode());

            // WebSocket推送
            BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
            info.setTriggerTime(mqttRoot.path("triggerTime").asText());
            info.setCreateTime(LocalDateTime.now().format(formatter));
            // MQTT模式下图片URL为空

            SasParkingEventCode code = parkingEventCodeMapper.selectById(event.getEventCode());
            if (code != null) {
                info.setEventTypeName(code.getName());
            }

            enrichDeviceInfo(info, device);
            SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.parking.getCode());
            if (eventTypeGroup != null) {
                info.setEventLevelId(eventTypeGroup.getEventLevel());
            }
            info.setDeviceType(SystemTypeCodeEnum.parking.getCode());
            SasWebSocket.sendAll(info);

                } catch (Exception e) {
            log.error("【车辆抓拍-MQTT】处理失败", e);
        }
    }

    /**
     * 使用HTTP获取的详细信息处理门禁事件
     */
    private void processEntranceEventWithHttpInfo(EntranceEventInfo eventInfo, EntranceEventMessage mqttMessage) {
        try {
            SasDevice device = findDevice(eventInfo.getDeviceId(), SystemTypeCodeEnum.entrance.getCode());
            if (device == null) {
                log.warn("【门禁-HTTP】设备不存在，丢弃事件：deviceId={}", eventInfo.getDeviceId());
                return;
            }

            String eventId = StrUtil.isNotBlank(eventInfo.getId()) ? eventInfo.getId() : 
                           (StrUtil.isNotBlank(mqttMessage.getId()) ? mqttMessage.getId() : IdUtil.randomUUID());
            if (entranceEventService.getById(eventId) != null) {
                log.info("【门禁-HTTP】事件已存在，跳过保存：eventId={}", eventId);
                return;
            }

            SasEntranceEvent event = new SasEntranceEvent();
            event.setEventId(eventId);
            
            // 从HTTP响应中获取详细信息
            BeanUtil.copyProperties(eventInfo, event);
            event.setCardId(eventInfo.getCardId());
            event.setTriggerTime(parseTime(eventInfo.getTriggerTime()));
            event.setCreateTime(LocalDateTime.now());
            event.setUpdateTime(LocalDateTime.now());

            // 处理HTTP中的图片信息
            if (eventInfo.getEventPic() != null) {
                try {
                    SasPic pic = new SasPic();
                    pic.setUrl(eventInfo.getEventPic().getUrl());
                    pic.setPath(eventInfo.getEventPic().getPath());
                    pic.setId(IdUtil.randomUUID());
                    pic.setCreateTime(LocalDateTime.now());
                    pic.setUpdateTime(LocalDateTime.now());
                    sasPicMapper.insert(pic);
                    event.setPicId(pic.getId());
                    log.info("【门禁-HTTP】图片保存成功，picId={}", pic.getId());
                } catch (Exception e) {
                    log.error("【门禁-HTTP】图片保存失败", e);
                    // 图片保存失败不影响主流程
                }
            }

            // 保存到数据库
            entranceEventService.save(event);
            log.info("【门禁-HTTP】保存成功！eventId={}, deviceId={}, eventCode={}",
                    eventId, eventInfo.getDeviceId(), eventInfo.getEventCode());

            // WebSocket推送
            BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
            info.setTriggerTime(eventInfo.getTriggerTime());
            info.setCreateTime(LocalDateTime.now().format(formatter));

            // 设置图片URL
            if (event.getPicId() != null) {
                SasPic pic = sasPicMapper.selectById(event.getPicId());
                if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                    info.setEventUrl(pic.getUrl() + pic.getPath());
                    log.debug("【门禁-HTTP】图片URL设置成功：{}", info.getEventUrl());
                }
            }

            SasEntranceEventCode code = entranceEventCodeMapper.selectById(eventInfo.getEventCode());
            if (code != null) {
                info.setEventTypeName(code.getName());
            }

            enrichDeviceInfo(info, device);
            SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.entrance.getCode());
            if (eventTypeGroup != null) {
                info.setEventLevelId(eventTypeGroup.getEventLevel());
            }
            info.setDeviceType(SystemTypeCodeEnum.entrance.getCode());
            SasWebSocket.sendAll(info);

        } catch (Exception e) {
            log.error("【门禁-HTTP】处理失败", e);
        }
    }

    /**
     * 使用MQTT基础信息处理门禁事件（降级方案）
     */
    private void processEntranceEventWithMqttInfo(EntranceEventMessage message) {
        try {
            SasDevice device = findDevice(message.getDeviceId(), SystemTypeCodeEnum.entrance.getCode());
            if (device == null) {
                log.warn("【门禁-MQTT】设备不存在，丢弃门禁事件: deviceId={}", message.getDeviceId());
                return;
            }

            SasEntranceEvent event = new SasEntranceEvent();
            String eventId = message.getId() != null && !message.getId().isEmpty()
                    ? message.getId()
                    : IdUtil.randomUUID();
            // 如果该事件已存在（设备重发/重复消息），避免主键冲突，直接跳过
            if (entranceEventService.getById(eventId) != null) {
                log.info("【门禁-MQTT】门禁事件已存在, 跳过保存, eventId={}", eventId);
                return;
            }
            event.setEventId(eventId);
            BeanUtil.copyProperties(message, event, "eventId");
            event.setCardId(message.getIcId());
            event.setTriggerTime(parseTime(message.getTriggerTime()));
            event.setCreateTime(LocalDateTime.now());
            event.setUpdateTime(LocalDateTime.now());

            // 处理图片信息
            if (message.getEntrancePic() != null) {
                EntranceEventMessage.PicInfo picInfo = message.getEntrancePic();
                if (picInfo != null && StrUtil.isNotBlank(picInfo.getUrl()) && StrUtil.isNotBlank(picInfo.getPath())) {
                    SasPic pic = new SasPic();
                    pic.setId(IdUtil.randomUUID());
                    pic.setUrl(picInfo.getUrl());
                    pic.setPath(picInfo.getPath());
                    pic.setCreateTime(LocalDateTime.now());
                    pic.setUpdateTime(LocalDateTime.now());
                    sasPicMapper.insert(pic);
                    
                    // 设置事件图片ID
                    event.setPicId(pic.getId());
                }
            }

            entranceEventService.save(event);

            BrieflyEventInfo info = BeanUtil.toBean(event, BrieflyEventInfo.class);
            info.setTriggerTime(message.getTriggerTime());
            info.setCreateTime(LocalDateTime.now().format(formatter));
            
            // 设置图片URL（如果有）
            if (event.getPicId() != null) {
                SasPic pic = sasPicMapper.selectById(event.getPicId());
                if (pic != null && StrUtil.isNotBlank(pic.getUrl()) && StrUtil.isNotBlank(pic.getPath())) {
                    info.setEventUrl(pic.getUrl() + pic.getPath());
                }
            }
            
            SasEntranceEventCode code = entranceEventCodeMapper.selectById(event.getEventCode());
            if (code != null) {
                info.setEventTypeName(code.getName());
            }

            enrichDeviceInfo(info, device);
            SasEventTypeGroup eventTypeGroup = findEventTypeGroup(SystemTypeCodeEnum.entrance.getCode());
            if (eventTypeGroup != null) {
                info.setEventLevelId(eventTypeGroup.getEventLevel());
            }
            info.setDeviceType(SystemTypeCodeEnum.entrance.getCode());
            SasWebSocket.sendAll(info);

            log.info("【门禁-MQTT】门禁事件保存并推送成功, eventId={}", event.getEventId());
        } catch (Exception e) {
            log.error("【门禁-MQTT】处理门禁事件失败", e);
        }
    }
}