package com.czg.mqtt.manager;

import com.alibaba.fastjson2.JSONObject;
import com.czg.config.RabbitPublisher;
import com.czg.system.service.SysDevicesService;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 设备在线状态管理器（单例）
 * 维护 clientId → 设备状态的映射，线程安全
 *
 * @author yjjie
 * @date 2025/11/18 15:33
 */
@Slf4j
@Component
public class DeviceStatusManager {
    // 并发安全的 Map：key=clientId（设备唯一标识），value=设备状态
    private final Map<String, DeviceStatus> deviceStatusMap = new ConcurrentHashMap<>();

    @Resource
    private SysDevicesService sysDevicesService;

    @Resource
    private RabbitPublisher rabbitPublisher;

    /**
     * 设备状态实体
     */
    @Data
    public static class DeviceStatus {
        private String clientId;       // 设备唯一标识
        private boolean online;        // 在线状态（true=在线，false=离线）
        private long lastPingTime;     // 最后一次 Ping 时间（时间戳，毫秒）
        private long connectTime;      // 连接建立时间（时间戳，毫秒）
        private String remoteAddress;  // 设备IP:端口

        public DeviceStatus(String clientId, String remoteAddress) {
            this.clientId = clientId;
            this.online = true;
            this.connectTime = System.currentTimeMillis();
            this.lastPingTime = this.connectTime; // 初始 Ping 时间=连接时间
            this.remoteAddress = remoteAddress;
        }
    }

    /**
     * 设备连接成功时：注册设备状态
     */
    public void registerDevice(String clientId, String remoteAddress) {
        DeviceStatus status = new DeviceStatus(clientId, remoteAddress);
        deviceStatusMap.put(clientId, status);
        log.info("设备注册上线：clientId={}, 地址={}", clientId, remoteAddress);

        sysDevicesService.updateDeviceOnlineStatus(clientId, 1);
    }

    /**
     * 收到 Ping 消息时：更新设备最后活跃时间（维持在线状态）
     */
    public void updateDevicePing(String clientId) {
        DeviceStatus status = deviceStatusMap.get(clientId);
        if (status != null) {
            status.setLastPingTime(System.currentTimeMillis());
            // 刷新为在线状态
            status.setOnline(true);

            log.info("设备上线：clientId={}, 最后Ping时间={}", clientId, status.getLastPingTime());
            sysDevicesService.updateDeviceOnlineStatus(clientId, 1);
        }
    }

    /**
     * 设备断开连接时：标记设备离线
     */
    public void markDeviceOffline(String clientId) {
        DeviceStatus status = deviceStatusMap.get(clientId);
        if (status != null) {
            status.setOnline(false);
            System.out.printf("设备离线：clientId=%s%n", clientId);
            log.info("设备下线：clientId={}, 最后Ping时间={}", clientId, status.getLastPingTime());

            deviceStatusMap.remove(clientId);
            sysDevicesService.updateDeviceOnlineStatus(clientId, 0);
        }
    }

    /**
     * 查询设备状态
     */
    public DeviceStatus getDeviceStatus(String clientId) {
        return deviceStatusMap.get(clientId);
    }

    public void sendMqMsg(String content) {
        JSONObject jsonObject = JSONObject.parseObject(content);
        rabbitPublisher.sendOrderProductStatusMsg(jsonObject.getString("msg"));
    }

    /**
     * 定时清理长时间离线的设备
     * offlineThresholdMs = 4 * 60 * 1000 = 14400000 毫秒 = 4分钟
     */
    @Scheduled(cron = "0 * * * * ?") // 每分钟第0秒执行（如 10:01:00、10:02:00）
    public void cleanOfflineDevices() {
        // 4分钟无Ping则清理
        long offlineThresholdMs = 4 * 60 * 1000;
        long now = System.currentTimeMillis();

        deviceStatusMap.entrySet().removeIf(entry -> {
            DeviceStatus status = entry.getValue();
            //  最后一次Ping距今超过4分钟
            boolean needRemove = (now - status.getLastPingTime()) > offlineThresholdMs;

            if (needRemove) {
                markDeviceOffline(entry.getKey());
                log.info("清理离线设备：clientId={}, 最后Ping时间={}, 离线时长={}",
                        entry.getKey(),
                        status.getLastPingTime(),
                        (now - status.getLastPingTime()) / 1000);
            }
            return needRemove;
        });
    }
}
