package org.dromara.app.task;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.app.domain.AppDevice;
import org.dromara.app.handler.MqttDeviceStatusService;
import org.dromara.app.mapper.AppDeviceMapper;
import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * 设备离线检测定时任务
 * <p>
 * 逻辑：电量上报刷新 Redis 在线 Key，Key 超时后判定设备离线。
 * 设备 LWT 离线消息仍可立即更新离线状态，本任务用于处理未收到遗嘱的异常断线。
 * 本任务定期扫描数据库中非离线状态的设备，如果对应 Redis Key 已不存在，
 * 则将数据库 status 更新为 0（离线）。
 * </p>
 *
 * <p>扫描间隔通过 {@code mqtt.offline-check.interval-ms} 配置，建议小于设备在线 Key 的 TTL。</p>
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DeviceOfflineCheckTask {

    private final AppDeviceMapper appDeviceMapper;
    private final MqttDeviceStatusService deviceStatusService;

    @Value("${mqtt.command-ack.device-status-cache-prefix}")
    private String deviceStatusCachePrefix;

    @Value("${mqtt.command-ack.device-status-cache-ttl-seconds:600}")
    private long deviceStatusCacheTtlSeconds;

    @Value("${mqtt.offline-check.enabled:true}")
    private boolean enabled;
    @Value("${mqtt.offline-check.ttl-compat-enabled:true}")
    private boolean ttlCompatEnabled;

    /**
     * 每 30 秒检查一次电量心跳是否超时。
     * 可通过 mqtt.offline-check.interval-ms 覆盖（单位 ms）
     */
    @Scheduled(fixedDelayString = "${mqtt.offline-check.interval-ms:30000}")
    public void checkOfflineDevices() {
        if (!enabled || !ttlCompatEnabled) {
            return;
        }

        // 查询数据库中所有非离线状态的设备，避免状态不是 1 但仍需离线修正的设备被漏掉
        List<AppDevice> onlineDevices = appDeviceMapper.selectList(
            new LambdaQueryWrapper<AppDevice>()
                .ne(AppDevice::getStatus, "0")
                .select(AppDevice::getDeviceNo)
        );

        if (onlineDevices.isEmpty()) {
            return;
        }

        List<String> offlineDeviceNos = new ArrayList<>();
        int migratedPermanentKeys = 0;
        for (AppDevice device : onlineDevices) {
            String deviceNo = device.getDeviceNo();
            String statusKey = deviceStatusCachePrefix + deviceNo;
            if (!RedisUtils.hasKey(statusKey)) {
                offlineDeviceNos.add(deviceNo);
                continue;
            }

            long ttl = RedisUtils.getTimeToLive(statusKey);
            if (ttl == -1L) {
                RedisUtils.expire(statusKey, Duration.ofSeconds(deviceStatusCacheTtlSeconds));
                migratedPermanentKeys++;
            } else if (ttl == -2L) {
                offlineDeviceNos.add(deviceNo);
            }
        }

        if (migratedPermanentKeys > 0) {
            log.info("[设备离线] 已为旧版永久在线缓存补充 TTL，设备数：{}", migratedPermanentKeys);
        }

        if (offlineDeviceNos.isEmpty()) {
            log.debug("[设备离线] 本次检测未发现离线设备，扫描设备数：{}", onlineDevices.size());
            return;
        }

        List<String> updatedDeviceNos = new ArrayList<>();
        for (String deviceNo : offlineDeviceNos) {
            if (deviceStatusService.markOfflineIfCacheMissing(deviceNo, "设备电量心跳超时")) {
                updatedDeviceNos.add(deviceNo);
            }
        }

        if (!updatedDeviceNos.isEmpty()) {
            log.info("[设备离线] 检测到 {} 台设备离线，已更新数据库状态，设备编号：{}",
                updatedDeviceNos.size(), updatedDeviceNos);
        }
    }
}
