package com.agri.quartz.task;

import com.agri.framework.interceptor.FrontendControlHandler;
import com.agri.framework.manager.AgriStatusManager;
import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysMessage;
import com.agri.system.service.AgriService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.agri.framework.config.MqttConfig;
import com.agri.system.service.ISysAgriInfoService;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 在线状态监测  告警和离线
 */
@Component
public class AgriStatusTask {

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

    // Redis 前缀常量
    private static final String SUB_KEY_PREFIX = "sub:";
    private static final String LOCK_KEY = "lock:device:online:push";

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Value("${spring.mqtt.dtu-ctl-lock-ttl:60}")
    private int lockTtl;

    @Autowired
    private ISysAgriInfoService agriInfoService;

    @Autowired
    private AgriStatusManager agriStatusManager;
    @Autowired
    private AgriService agriService;

    @Resource
    private FrontendControlHandler frontendControlHandler;

    /**
     * 定时任务：每10秒执行一次
     * 1. 扫描所有 sub: 开头的 key，提取 IMEI
     * 2. 批量查询设备在线状态
     * 3. 异步批量推送在线状态到 MQTT 主题 frontend/{imei}/online
     */
    public void pushDeviceStatus() {
        // 1. 加分布式锁，避免集群重复执行
        Boolean lockSuccess = stringRedisTemplate.opsForValue()
                .setIfAbsent(LOCK_KEY, "running", lockTtl, TimeUnit.SECONDS);

        // 补充：处理Redis连接异常的情况
        if (lockSuccess == null) {
            log.error("获取分布式锁失败：Redis连接异常");
            return;
        }
        if (!lockSuccess) {
            log.debug("其他节点正在执行，跳过本次推送");
            return;
        }
        long startTime = System.currentTimeMillis();
        try {

            // 查询大棚列表所有在线设备
            List<SysAgriInfo> agriInfos = agriInfoService.findAgriByUser(new SysAgriInfo());
            List<String> imeiList = agriService.queryAllGreenhouseImei(agriInfos);
            if (imeiList.isEmpty()) {
                log.info("大棚表无数据，结束推送");
                return;
            }
            Map<String, Map<String, Boolean>> statusMap
                    = agriStatusManager.batchCheckDeviceOnline(imeiList);
            // 3. 首页状态
            agriStatusManager.asyncBatchPushMqtt(statusMap);
            // 4. 离线设备
            List<SysAgriInfo> offlineDevices = findOfflineDevices(agriInfos, statusMap);
            // 5. 保存消息中心
            List<SysMessage> messages = agriService.saveMessage(offlineDevices);
            // 6. 发送告警消息
            frontendControlHandler.sendAlarmMessage(messages);

        } catch (Exception e) {
            log.error("设备在线状态推送任务异常", e);
            // 可选：异常告警（如企业微信/钉钉）
            // WxUtil.pushText("【设备在线状态推送异常】\n" + e.getMessage());
        } finally {
            // 释放锁（可选，也可依赖TTL自动过期）
            stringRedisTemplate.delete(LOCK_KEY);
        }
    }
    // 查找离线设备
    private List<SysAgriInfo> findOfflineDevices(List<SysAgriInfo> agriInfos,
                                                 Map<String, Map<String, Boolean>> statusMap) {
        if (statusMap.isEmpty()) {
            log.info("不存在任何imei");
            return new ArrayList<>();
        }
        Map<String, Object> offlineMap = new HashMap<>();
        for (Map.Entry<String, Map<String, Boolean>> map : statusMap.entrySet()) {
            if (!map.getValue().get("imeiOnline")) {
                offlineMap.put(map.getKey(), map.getValue());
            }
        }
        List<SysAgriInfo> offlineDevices = new ArrayList<>();
        for (SysAgriInfo agriInfo : agriInfos) {
            if (!offlineMap.containsKey(agriInfo.getImei())) {
                agriInfo.setTitle("设备离线告警");
                agriInfo.setMsg("怀疑设备离线！请及时检查");
                offlineDevices.add(agriInfo);
                log.info("设备{} 不存在设备状态", agriInfo.getImei());
            }
        }
        return offlineDevices;
    }

    /*
     * 企业级Lua方案：基于大棚表IMEI列表，批量查sub:{imei}是否存在
     * 核心：1次Redis网络往返完成所有查询，极致性能 + 数据合法
     * @return 合法大棚IMEI的在线状态（key=IMEI，value=是否在线）
     */
    public Map<String, Boolean> getGreenhouseOnlineStatusByLua() {
        // 1. 从大棚表获取所有合法IMEI
        List<String> allGreenhouseImeiList = agriInfoService.queryImeiByUserId(null);
        if (allGreenhouseImeiList.isEmpty()) {
            log.info("大棚表无合法IMEI，返回空");
            return new HashMap<>();
        }

        // 2. 简化版Lua脚本（批量查sub:{imei}是否存在）
        String luaScript = "" +
                "local imeiList = ARGV\n" +
                "local prefix = KEYS[1]\n" +
                "local result = {}\n" +
                "for _, imei in ipairs(imeiList) do\n" +
                "    local key = prefix .. imei\n" +
                "    local isOnline = redis.call('EXISTS', key)\n" +
                "    table.insert(result, imei)\n" +
                "    table.insert(result, tostring(isOnline))\n" +
                "end\n" +
                "return result";

        // ===== 核心修改：保留你指定的写法，仅做JDK 8兼容 =====
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(List.class); // 保留原有写法

        // 3. 构造参数
        List<String> keys = Collections.singletonList(SUB_KEY_PREFIX);
        List<String> args = allGreenhouseImeiList;

        try {
            // 4. 执行脚本（JDK 8 强制类型转换，安全兼容）
            List resultList = stringRedisTemplate.execute(redisScript, keys, args.toArray(new String[0]));

            // 5. 解析结果（兼容JDK 8 原始List类型）
            Map<String, Boolean> onlineStatusMap = new HashMap<>();
            if (resultList != null && resultList.size() >= 2) {
                for (int i = 0; i < resultList.size(); i += 2) {
                    // JDK 8 显式转换为String，避免类型异常
                    String imei = String.valueOf(resultList.get(i));
                    String isOnlineStr = String.valueOf(resultList.get(i + 1));
                    onlineStatusMap.put(imei, "1".equals(isOnlineStr));
                }
            }

            return onlineStatusMap;
        } catch (Exception e) {
            log.error("Lua脚本执行失败", e);
            return new HashMap<>();
        }
    }


    // ========== 方式1：用 ScanCursor 遍历 sub:* Key  ==========
    private List<String> scanAllSubDeviceImei() {
        List<String> imeiList = new ArrayList<>();
        // count  建议一次扫描1000个槽位
        ScanOptions scanOptions = ScanOptions.scanOptions()
                .match(SUB_KEY_PREFIX + "*")
                .count(1000)
                .build();

        // JDK 8 需显式声明 Cursor<byte[]> 泛型，try-with-resources 自动关闭游标
        try (Cursor<byte[]> cursor = stringRedisTemplate.getConnectionFactory()
                .getConnection()
                .scan(scanOptions)) {

            while (cursor.hasNext()) {
                byte[] keyBytes = cursor.next();
                String key = new String(keyBytes, StandardCharsets.UTF_8);
                if (key.startsWith(SUB_KEY_PREFIX)) {
                    String imei = key.substring(SUB_KEY_PREFIX.length());
                    imeiList.add(imei);
                }
            }
        } catch (Exception e) {
            log.error("Cursor 扫描 sub: 前缀 key 失败", e);
        }
        return imeiList;
    }
}
