package com.agri.framework.manager;

import com.agri.common.utils.wechat.WxUtil;
import com.agri.framework.config.MqttConfig;
import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysDevOperLog;
import com.agri.system.service.ISysAgriInfoService;
import com.agri.system.service.ISysDevOperLogService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.lang3.ObjectUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
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.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 设备自动关任务管理器
 * 核心功能：
 * 1. 初始化/优雅关闭自动关任务线程池
 * 2. 调度/取消设备功能自动关闭任务
 * 3. 执行自动关任务，校验设备最新状态
 * 4. 维护设备未完成自动关任务计数
 * 改造点：自动关闭任务改为多线程并行执行
 * 适配JDK 8，无心跳包相关逻辑
 */
@Component
public class MqttAutoOffManager {
    /**
     * 优化：统一使用SLF4J日志（JDK 8兼容）
     */
    private static final Logger log = LoggerFactory.getLogger(MqttAutoOffManager.class);

    /**
     * Redis模板，用于存储订阅关系、设备在线状态、分布式锁
     */
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * MQTT消息发送工具类（由MqttConfig配置类注入）
     */
    @Resource
    private MqttConfig.MqttMessageSender mqttMessageSender;

    // 改造：将单线程池改为固定线程池，支持多任务并行执行
    // 替代原有的 Executors.newSingleThreadScheduledExecutor()
    private ScheduledExecutorService autoOffExecutor;

    // 新增：同设备同功能只保留最后一次自动关任务
    private final ConcurrentHashMap<String, ScheduledFuture<?>> autoOffFutureMap = new ConcurrentHashMap<>();

    // 新增：按设备维度统计“未完成的自动关任务”数量，hasAutoOffTask从扫描O(N)降为O(1)
    private final ConcurrentHashMap<String, Integer> autoOffDeviceCnt = new ConcurrentHashMap<>();

    // 新增：自动关闭任务线程池核心线程数（可配置）
    @Value("${spring.mqtt.auto-off-thread-pool-size:5}")
    private int autoOffThreadPoolSize;

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

    @Autowired
    private ISysAgriInfoService sysAgriInfoService;
    @Autowired
    private ISysDevOperLogService sysDevOperLogService;


    /**
     * 初始化自动关任务线程池
     * @param corePoolSize 核心线程数
     */
    public void initExecutor(int corePoolSize) {
        // 初始化多线程池（固定线程数）
        autoOffExecutor = new ScheduledThreadPoolExecutor(
                autoOffThreadPoolSize, // 核心线程数
                r -> {
                    Thread thread = new Thread(r);
                    thread.setName("auto-off-task-" + thread.getId());
                    thread.setDaemon(true); // 设置为守护线程，不阻塞JVM退出
                    return thread;
                },
                new ThreadPoolExecutor.CallerRunsPolicy() // 队列压力或关闭时兜底不丢任务
        );

        // 关键优化1：取消任务后立即从队列移除，避免队列堆积
        ((ScheduledThreadPoolExecutor) autoOffExecutor).setRemoveOnCancelPolicy(true);

        // 关键优化2：允许核心线程超时回收，空闲时省资源
        ((ScheduledThreadPoolExecutor) autoOffExecutor).setKeepAliveTime(60, TimeUnit.SECONDS);
        ((ScheduledThreadPoolExecutor) autoOffExecutor).allowCoreThreadTimeOut(true);

        log.info("自动关任务线程池初始化完成，核心线程数={}", corePoolSize);
    }

    /**
     * 优雅关闭自动关任务线程池
     */
    public void shutdownExecutor() {
        try {
            // 1. 取消所有未执行的自动关闭任务
            for (Map.Entry<String, ScheduledFuture<?>> entry : autoOffFutureMap.entrySet()) {
                entry.getValue().cancel(false);
                log.debug("【自动关任务】取消任务：{}", entry.getKey());
            }
            autoOffFutureMap.clear();
            // ✅ 停止时直接清空计数，避免残留
            autoOffDeviceCnt.clear();

            // 2. 优雅关闭线程池
            if (autoOffExecutor != null) {
                autoOffExecutor.shutdown();
                try {
                    // 等待3秒让任务完成
                    if (!autoOffExecutor.awaitTermination(3, TimeUnit.SECONDS)) {
                        // 强制关闭
                        autoOffExecutor.shutdownNow();
                        log.warn("【自动关任务】线程池强制关闭");
                    }
                } catch (InterruptedException e) {
                    autoOffExecutor.shutdownNow();
                    Thread.currentThread().interrupt();
                }
                log.info("【自动关任务】线程池已关闭");
            }
        } catch (Exception e) {
            log.error("【自动关任务】线程池关闭失败", e);
        }
    }

    // 新增：是否存在该设备的自动关任务
    public boolean hasAutoOffTask(String deviceId) {
        if (!StringUtils.hasText(deviceId)) {
            return false;
        }
        // ✅ O(1)查询，无需扫描整个任务表
        Integer cnt = autoOffDeviceCnt.get(deviceId);
        return cnt != null && cnt > 0;
    }

    // 新增：自动关任务计数 +1（只维护deviceId维度，确保hasAutoOffTask为O(1)）
    private void incAutoOffCnt(String deviceId) {
        if (!StringUtils.hasText(deviceId)) {
            return;
        }
        autoOffDeviceCnt.merge(deviceId, 1, (a, b) -> a + b);
    }

    // 新增：自动关任务计数 -1（避免负数；归零则清理key，省内存）
    private void decAutoOffCnt(String deviceId) {
        if (!StringUtils.hasText(deviceId)) {
            return;
        }
        autoOffDeviceCnt.compute(deviceId, (k, v) -> {
            if (v == null || v <= 1) {
                return null;
            }
            return v - 1;
        });
    }

    // 改造：多线程执行自动关闭任务
    // 起个任务，固定多少秒-n秒，【监听最新的设备状态，如果还在运行】，发送设备关的指令
    public void scheduleAutoOff(String deviceId, String funcType, int delaySeconds) {

        // ✅ 防御：避免极端情况下线程池尚未初始化导致NPE
        if (autoOffExecutor == null) {
            log.warn("【自动关任务】线程池未初始化，跳过创建任务：deviceId={}, funcType={}", deviceId, funcType);
            return;
        }

        String taskKey = "autooff:" + deviceId + ":" + funcType;

        cancelAutoOff(deviceId,funcType);

        // 使用多线程池提交任务
        ScheduledFuture<?> newFuture = autoOffExecutor.schedule(() -> {
            try {
                runAutoOff(deviceId, funcType);
            } catch (Exception e) {
                WxUtil.pushText("【自动关任务】提交任务失败! \n  deviceId: "+deviceId+"\n  funcType: "+funcType+"\n  cause: "+e);
                log.error("【自动关任务】执行失败，deviceId={}, funcType={}", deviceId, funcType, e);
            } finally {
                // 任务执行完成后移除映射
                autoOffFutureMap.remove(taskKey);
                // ✅ 任务结束（成功/失败都算结束）：减少该设备的“未完成任务数”，保证hasAutoOffTask准确
                decAutoOffCnt(deviceId);
            }
        }, delaySeconds, TimeUnit.SECONDS);

        // 保存新任务的引用
        autoOffFutureMap.put(taskKey, newFuture);
        // ✅ 新任务创建成功：增加该设备的“未完成任务数”
        incAutoOffCnt(deviceId);
        log.info("【当前任务队列】：{}",autoOffDeviceCnt);
        log.info("【自动关任务】已创建（多线程）：deviceId={}, funcType={}, delay={}s", deviceId, funcType, delaySeconds);
    }

    // 自动关闭任务的核心逻辑（无改动）
    // 新增：读取最新状态（device:latest:{deviceId}），若仍为1则下发 {"funcType":0} 到 dtu/{id}/down
    private void runAutoOff(String deviceId, String funcType) throws MqttException {
        String latest = stringRedisTemplate.opsForValue().get("device:latest:" + deviceId);
        String skipReason = "";
        if (!StringUtils.hasText(latest)) {
            //todo
            log.warn("【自动关任务】无最新状态，跳过：deviceId={}, funcType={}", deviceId, funcType);
            return;
        }
        JSONObject latestObj;
        try {
            latestObj = JSON.parseObject(latest);
        } catch (Exception e) {
            WxUtil.pushText("自动关任务执行报错-解析异常：\n  deviceId: " + deviceId + "\n  funcType:" + funcType+"\n  Cause: "+e);
            log.warn("【自动关任务】最新状态JSON解析失败，跳过：deviceId={}, funcType={}", deviceId, funcType);
            return;
        }
        if (latestObj == null || latestObj.isEmpty()) {
            log.info("【自动关任务】最新状态为空");
            skipReason = "【自动关任务】最新状态为空";
            return;
        }

        // 设备每10秒上报的状态包：{"jm1k":0/1,...} 顶层字段直接取
        Integer current = null;
        String key = getKey(funcType);
        try {
            if (latestObj.containsKey(key)) {
                current = latestObj.getIntValue(key);
            }
        } catch (Exception ignore) {
            skipReason = "【自动关任务】最新状态功能码获取失败";
        }
        if (current == null) {
            skipReason = "【自动关任务】状态未知";
        }
        if (current != 1) {
            skipReason = "【自动关任务】未运行";
        }
        sysDevOperLogService.lambdaUpdate()
            .eq(SysDevOperLog::getImei, deviceId)
            .eq(SysDevOperLog::getFuncCode, funcType)
            .eq(SysDevOperLog::getOpType, 1)
            .eq(SysDevOperLog::getOpSource, 1)
            .eq(SysDevOperLog::getAckSuc, 1)
            .eq(SysDevOperLog::getIsTask, 1)
            .orderByDesc(SysDevOperLog::getCreateTime)
            .last("LIMIT 1")
            .set(SysDevOperLog::getExecResult,skipReason.isEmpty()?1:0)
            .set(SysDevOperLog::getUpdateBy,"自动关更新数据")
            .set(SysDevOperLog::getSkipReason, skipReason.isEmpty()?"执行成功":skipReason)
            .update();
        if (!skipReason.isEmpty()) {
            log.info("【自动关任务】检测未运行或状态未知，跳过关闭：deviceId={}, funcType={}, current={}", deviceId, funcType, current);
            return;
        }

        // 新增：自动关也走分布式锁（避免与前端并发控制同一功能导致乱序/互相覆盖）
        String lockKey = "lock:" + deviceId + ":" + funcType;
        Boolean lockSuccess = stringRedisTemplate.opsForValue().setIfAbsent(
                lockKey, "autooff", dtuCtlLockTTL, TimeUnit.SECONDS
        );
        if (lockSuccess == null || !lockSuccess) {
            log.info("【自动关任务】{}功能忙（锁占用），跳过自动关闭：deviceId={}, funcType={}", funcType, deviceId, funcType);
            return;
        }
        try {
            String deviceTopic = "dtu/" + deviceId + "/down";
            JSONObject down = new JSONObject();
            down.put(funcType, 0);
            log.info("触发自动化条件");
            mqttMessageSender.publish(deviceTopic, down.toJSONString());
            saveDevLog(deviceId, funcType, down, latest);
            log.info("【自动关任务】检测仍在运行，已下发关闭：deviceId={}, funcType={}, payload={}", deviceId, funcType, down.toJSONString());
        } catch (Exception e){
            WxUtil.pushText("自动关任务执行报错-下发关闭指令：\n  deviceId: " + deviceId + "\n  funcType:" + funcType+"\n  Cause: "+e);
            log.warn("【自动关任务】下发关闭指令失败，跳过：deviceId={}, funcType={}", deviceId, funcType);
        } finally {
            stringRedisTemplate.delete(lockKey);
        }
    }

    private void saveDevLog(String deviceId, String funcType, JSONObject down, String latest) {
        SysAgriInfo agriInfo = sysAgriInfoService.lambdaQuery()
                .eq(SysAgriInfo::getImei, deviceId)
                .one();
        String agriName = (agriInfo != null && ObjectUtils.isNotEmpty(agriInfo.getAgriName())) ? agriInfo.getAgriName() : null;
        SysDevOperLog logDto = new SysDevOperLog();
        logDto.setAgriName(agriName);
        logDto.setImei(deviceId);
        logDto.setFuncCode(funcType);
        logDto.setOpType(0);
        logDto.setOpSource(2);
        logDto.setPayload(down.toJSONString());
        logDto.setLockAcquired(1);
        logDto.setLockHolder("autoOff");
        logDto.setExecResult(1);
        logDto.setLatestState(latest);
        logDto.setCreateBy("自动关");
        logDto.setTaskStatus(getFutureStatus().toString());
        sysDevOperLogService.save(logDto);
    }

    // 新增：收到“关”指令时，尝试取消对应自动关任务（优化：减少无意义任务执行；正确性仍以到点状态判断为准）
    public void cancelAutoOff(String deviceId, String funcType) {
        if (!StringUtils.hasText(deviceId) || !StringUtils.hasText(funcType)) {
            return;
        }
        String taskKey = "autooff:" + deviceId + ":" + funcType;
        // 同设备同功能只保留最后一次任务：只有旧任务还没开始时才替换
        ScheduledFuture<?> oldFuture = autoOffFutureMap.get(taskKey);
        if (oldFuture != null) {
            log.info("存在定时任务：taskKey: "+taskKey);
            // cancel=false 说明任务已开始/已完成，避免双执行：不再创建新任务
            if (!oldFuture.cancel(false)) {
                return;
            }
            sysDevOperLogService.lambdaUpdate()
                    .eq(SysDevOperLog::getImei, deviceId)
                    .eq(SysDevOperLog::getFuncCode, funcType)
                    .eq(SysDevOperLog::getOpType, 1)
                    .eq(SysDevOperLog::getOpSource, 1)
                    .eq(SysDevOperLog::getAckSuc, 1)
                    .eq(SysDevOperLog::getIsTask, 1)
                    .orderByDesc(SysDevOperLog::getCreateTime)
                    .last("LIMIT 2")
                    .set(SysDevOperLog::getExecResult,2)
                    .set(SysDevOperLog::getLatestState, "{\"msg\":\"任务已退出\"}")
                    .set(SysDevOperLog::getUpdateBy,"自动关")
                    .set(SysDevOperLog::getSkipReason, "旧任务已退出")
                    .update();
            // cancel成功：旧任务不会跑了，这时再remove并减计数
            autoOffFutureMap.remove(taskKey, oldFuture);
            decAutoOffCnt(deviceId);
            log.info("旧任务已退出："+taskKey);
        } else {
            log.info("不存在定时任务：taskKey: "+taskKey);
        }
    }

    /**
    * 判断是否有当前imei的功能任务
     * 担忧点、、、、自动模式下，上一次指令运行时间过长，本次触发指令是否取消本次任务
     * 但是目前自动化任务改为10分钟一执行、前端运行时间也就是风口限制最大只允许运行5分钟，自动模式下也是不允许超过300秒
     * 因此按理说，此后应该不会出现此类问题
     * @param deviceId
     * @param funcType
     * @return
     */
    public boolean isHaveTask(String deviceId, String funcType) {
        String taskKey = "autooff:" + deviceId + ":" + funcType;
        // 同设备同功能只保留最后一次任务：只有旧任务还没开始时才替换
        ScheduledFuture<?> oldFuture = autoOffFutureMap.get(taskKey);
        return oldFuture != null;
    }
    // 自动关是否启用（你可以先写死 true / false）
    public boolean isEnabled() {
        return true; // 之后可接配置
    }

    // 线程池是否初始化
    public boolean isExecutorInited() {
        return autoOffExecutor != null;
    }

    // 当前所有未完成任务数
    public int getTotalTaskCount() {
        return autoOffFutureMap.size();
    }

    // 当前有自动关任务的设备数量
    public int getDeviceTaskCount() {
        return autoOffDeviceCnt.size();
    }

    public JSONObject getFutureStatus() {
        JSONObject json = new JSONObject();
                json.put("isEnabled", true);
                json.put("isExecutorInited", autoOffExecutor != null);
                json.put("getTotalTaskCount", autoOffFutureMap.size());
                json.put("getDeviceTaskCount", autoOffDeviceCnt.size());
        return json;
    }


    public String getKey(String funcType) {
        // 边界判断：避免空指针异常和字符串长度不足的异常
        if (funcType == null || funcType.length() <= 1) {
            return funcType == null ? null : "";
        }

        // 截取：从索引0开始，到倒数第二位结束（substring的结束索引是开区间，不包含自身）
        return funcType.substring(0, funcType.length() - 1);
    }
}