/*
 * 文 件 名:  PollingMonitorService
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  <描述>
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/12/11
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */
package com.ruoyi.ems.strategy;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.ems.domain.EmsObjAttrValue;
import com.ruoyi.ems.domain.OpEnergyStrategy;
import com.ruoyi.ems.domain.OpEnergyStrategyTrigger;
import com.ruoyi.ems.model.AbilityPayload;
import com.ruoyi.ems.service.IAbilityCallService;
import com.ruoyi.ems.service.IEmsObjAttrValueService;
import com.ruoyi.ems.service.IOpEnergyStrategyService;
import com.ruoyi.ems.service.IOpEnergyStrategyTriggerService;
import com.ruoyi.ems.strategy.evaluator.ConditionEvaluator;
import com.ruoyi.ems.strategy.executor.StrategyExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * 轮询监控服务
 * <功能详细描述>
 * 与属性变化触发的区别：
 * - 属性变化触发：被动等待采集模块推送变化
 * - 轮询监控触发：主动定期查询并判断
 *
 * @author lvwenbin
 * @version [版本号, 2025/12/11]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
@Component
public class PollingMonitorService {

    @Autowired
    private IOpEnergyStrategyService strategyService;

    @Autowired
    private IOpEnergyStrategyTriggerService triggerService;

    @Autowired
    private IEmsObjAttrValueService attrValueService;

    @Autowired
    private ConditionEvaluator conditionEvaluator;

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 能力调用服务 - 用于下发主动查询指令
     * 复用 AbilityCallStepExecutor 中使用的服务
     */
    @Autowired
    private IAbilityCallService abilityCallService;

    /**
     * 延迟加载 StrategyExecutor 避免循环依赖
     */
    private StrategyExecutor strategyExecutor;

    /**
     * 轮询任务调度器
     */
    private final ScheduledExecutorService pollingScheduler = Executors.newScheduledThreadPool(5);

    /**
     * 已注册的轮询任务
     * key: strategyCode
     * value: ScheduledFuture
     */
    private final Map<String, ScheduledFuture<?>> pollingTasks = new ConcurrentHashMap<>();

    /**
     * 轮询配置缓存
     * key: strategyCode
     * value: PollingConfig
     */
    private final Map<String, PollingConfig> pollingConfigs = new ConcurrentHashMap<>();

    /**
     * 上次属性值缓存（用于判断是否变化）
     * key: strategyCode + ":" + deviceCode + ":" + attrKey
     * value: 上次的属性值
     */
    private final Map<String, String> lastAttrValues = new ConcurrentHashMap<>();

    /**
     * 获取 StrategyExecutor（延迟加载）
     */
    private StrategyExecutor getStrategyExecutor() {
        if (strategyExecutor == null) {
            synchronized (this) {
                if (strategyExecutor == null) {
                    strategyExecutor = applicationContext.getBean(StrategyExecutor.class);
                }
            }
        }
        return strategyExecutor;
    }

    @PreDestroy
    public void destroy() {
        log.info("====== 轮询监控服务销毁 ======");
        pollingTasks.values().forEach(future -> future.cancel(true));
        pollingTasks.clear();
        pollingConfigs.clear();
        pollingScheduler.shutdown();
    }

    // ==================== 注册与注销 ====================

    /**
     * 注册轮询监控策略
     *
     * @param strategy 策略对象
     */
    public void registerPollingStrategy(OpEnergyStrategy strategy) {
        String strategyCode = strategy.getStrategyCode();

        // 获取策略的触发器配置
        List<OpEnergyStrategyTrigger> triggers = triggerService.selectByStrategyCode(strategyCode);

        // 查找轮询类型的触发器
        OpEnergyStrategyTrigger pollingTrigger = null;
        for (OpEnergyStrategyTrigger trigger : triggers) {
            if (trigger.getEnable() == 1 && "POLLING".equals(trigger.getTriggerType())) {
                pollingTrigger = trigger;
                break;
            }
        }

        // 如果没有专门的轮询触发器，检查是否是条件触发(4)且配置了轮询
        if (pollingTrigger == null && strategy.getTriggerType() == 4) {
            // 从触发器中查找 ATTR 类型，并检查是否配置了轮询
            for (OpEnergyStrategyTrigger trigger : triggers) {
                if (trigger.getEnable() == 1 && "ATTR".equals(trigger.getTriggerType())) {
                    // 检查条件表达式中是否配置了轮询
                    PollingConfig config = parsePollingConfig(trigger);
                    if (config != null && config.pollingEnabled) {
                        pollingTrigger = trigger;
                        break;
                    }
                }
            }
        }

        if (pollingTrigger == null) {
            log.debug("策略[{}]未配置轮询触发器", strategyCode);
            return;
        }

        // 解析轮询配置
        PollingConfig config = parsePollingConfig(pollingTrigger);
        if (config == null || !config.pollingEnabled) {
            log.warn("策略[{}]轮询配置解析失败或未启用", strategyCode);
            return;
        }

        // 先取消已有的轮询任务
        unregisterPollingStrategy(strategyCode);

        // 保存配置
        pollingConfigs.put(strategyCode, config);

        // 创建轮询任务
        ScheduledFuture<?> future = pollingScheduler.scheduleWithFixedDelay(
            () -> executePollingCheck(strategyCode, config), config.initialDelay, config.pollingInterval,
            TimeUnit.MILLISECONDS);

        pollingTasks.put(strategyCode, future);

        log.info("策略[{}]已注册轮询监控: device={}, attr={}, interval={}ms, activeQuery={}, queryAbility={}",
            strategyCode, config.deviceCode, config.attrKey, config.pollingInterval, config.activeQuery,
            config.queryAbilityKey);
    }

    /**
     * 注销轮询监控策略
     */
    public void unregisterPollingStrategy(String strategyCode) {
        ScheduledFuture<?> future = pollingTasks.remove(strategyCode);
        if (future != null) {
            future.cancel(true);
            log.info("策略[{}]的轮询监控已注销", strategyCode);
        }
        pollingConfigs.remove(strategyCode);

        // 清理相关的缓存
        lastAttrValues.entrySet().removeIf(entry -> entry.getKey().startsWith(strategyCode + ":"));
    }

    /**
     * 刷新策略的轮询配置
     */
    public void refreshPollingStrategy(String strategyCode) {
        OpEnergyStrategy strategy = strategyService.selectStrategyByCode(strategyCode);
        if (strategy == null || strategy.getStrategyState() != 1) {
            unregisterPollingStrategy(strategyCode);
            return;
        }

        registerPollingStrategy(strategy);
    }

    // ==================== 轮询执行 ====================

    /**
     * 执行轮询检查
     */
    private void executePollingCheck(String strategyCode, PollingConfig config) {
        try {
            log.debug("执行轮询检查: strategy={}, device={}, attr={}", strategyCode, config.deviceCode, config.attrKey);

            // 1. 如果配置了主动查询，先下发查询指令
            if (config.activeQuery) {
                boolean querySuccess = executeActiveQuery(config);
                if (!querySuccess) {
                    log.warn("策略[{}]主动查询指令下发失败", strategyCode);
                    // 可以选择继续读取缓存值或跳过本次检查
                }
                // 等待设备响应
                Thread.sleep(config.queryWaitTime);
            }

            // 2. 从数据库读取最新属性值
            EmsObjAttrValue attrValue = attrValueService.selectObjAttrValue(config.modelCode, config.deviceCode,
                config.attrKey);

            if (attrValue == null) {
                log.debug("策略[{}]轮询: 属性值不存在 device={}, attr={}", strategyCode, config.deviceCode,
                    config.attrKey);
                return;
            }

            String currentValue = attrValue.getAttrValue();
            String cacheKey = strategyCode + ":" + config.deviceCode + ":" + config.attrKey;
            String lastValue = lastAttrValues.get(cacheKey);

            log.debug("策略[{}]轮询检查: attr={}, lastValue={}, currentValue={}", strategyCode, config.attrKey,
                lastValue, currentValue);

            // 3. 判断是否满足触发条件
            boolean shouldTrigger = evaluateTriggerCondition(config, currentValue, lastValue);

            // 4. 更新缓存
            lastAttrValues.put(cacheKey, currentValue);

            // 5. 如果满足条件，触发策略执行
            if (shouldTrigger) {
                log.info("策略[{}]轮询触发条件满足: device={}, attr={}, value={}", strategyCode, config.deviceCode,
                    config.attrKey, currentValue);

                triggerStrategyExecution(strategyCode, config, currentValue, lastValue);
            }

        }
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.warn("策略[{}]轮询被中断", strategyCode);
        }
        catch (Exception e) {
            log.error("策略[{}]轮询检查异常", strategyCode, e);
        }
    }

    /**
     * 评估触发条件
     */
    private boolean evaluateTriggerCondition(PollingConfig config, String currentValue, String lastValue) {
        // 构建条件评估上下文
        Map<String, Object> context = new HashMap<>();
        context.put(config.attrKey, currentValue);
        context.put("current_" + config.attrKey, currentValue);
        context.put("newValue", currentValue);
        context.put("oldValue", lastValue);

        // 使用条件求值器评估
        boolean conditionMet = conditionEvaluator.evaluate(config.conditionExpr, context);

        // 根据触发模式判断是否真正触发
        switch (config.triggerMode) {
            case "ALWAYS":
                // 只要条件满足就触发
                return conditionMet;

            case "ON_CHANGE":
                // 条件满足且值发生变化才触发
                return conditionMet && !currentValue.equals(lastValue);

            case "ON_FIRST_MATCH":
                // 条件满足且上次不满足才触发（边沿触发）
                if (!conditionMet) {
                    return false;
                }
                // 检查上次是否满足条件
                if (lastValue == null) {
                    return true;
                }
                Map<String, Object> lastContext = new HashMap<>();
                lastContext.put(config.attrKey, lastValue);
                boolean lastConditionMet = conditionEvaluator.evaluate(config.conditionExpr, lastContext);
                return !lastConditionMet;

            default:
                return conditionMet;
        }
    }

    /**
     * 执行主动查询（下发查询指令）
     * 复用 AbilityCallStepExecutor 中的能力调用逻辑：
     * 1. 构建 AbilityPayload 对象
     * 2. 调用 IAbilityCallService.devAbilityCall() 方法
     * 3. 设备会通过 MQTT 下发查询指令
     * 4. 设备回包后，由采集模块（如 Keka86BsHandler）更新数据库
     *
     * @param config 轮询配置（包含查询能力信息）
     * @return 是否下发成功
     */
    private boolean executeActiveQuery(PollingConfig config) {
        try {
            // 检查是否配置了查询能力
            if (config.queryAbilityKey == null || config.queryAbilityKey.isEmpty()) {
                log.warn("策略[{}]未配置查询能力(queryAbilityKey)", config.strategyCode);
                return false;
            }

            log.debug("下发主动查询指令: device={}, ability={}, param={}", config.deviceCode, config.queryAbilityKey,
                config.queryAbilityParam);

            // 构建能力调用参数（与 AbilityCallStepExecutor 相同）
            AbilityPayload payload = new AbilityPayload();
            payload.setObjCode(config.deviceCode);          // 目标设备编码
            payload.setObjType(2);                           // 2 = 设备类型
            payload.setModelCode(config.modelCode);          // 物模型编码
            payload.setAbilityKey(config.queryAbilityKey);   // 能力键（如 syncState）

            // 设置能力参数（如果有）
            if (config.queryAbilityParam != null && !config.queryAbilityParam.isEmpty()) {
                payload.setAbilityParam(config.queryAbilityParam);
            }

            // 调用设备能力服务（复用 AbilityCallStepExecutor 的逻辑）
            // 这里调用的是 IAbilityCallService.devAbilityCall()
            // 内部会通过 MQTT 下发指令到网关
            abilityCallService.devAbilityCall(payload);

            log.debug("主动查询指令下发成功: device={}, ability={}", config.deviceCode, config.queryAbilityKey);

            return true;

        }
        catch (Exception e) {
            log.error("主动查询指令下发失败: device={}, ability={}", config.deviceCode, config.queryAbilityKey, e);
            return false;
        }
    }

    /**
     * 触发策略执行
     * 修复：完善触发信息传递
     */
    private void triggerStrategyExecution(String strategyCode, PollingConfig config, String currentValue, String lastValue) {
        try {
            Map<String, Object> params = new HashMap<>();

            // 触发类型和触发源
            params.put("trigger_type", "POLLING");
            params.put("trigger_source", config.deviceCode + "." + config.attrKey);

            // 执行人（轮询监控为系统自动执行）
            params.put("exec_by", "SYSTEM_POLLING");

            // 设备和属性信息
            params.put("device_code", config.deviceCode);
            params.put("model_code", config.modelCode);
            params.put("attr_key", config.attrKey);
            params.put("current_value", currentValue);
            params.put("old_value", lastValue);

            // 方便上下文变量访问
            params.put(config.attrKey, currentValue);
            params.put("current_" + config.attrKey, currentValue);

            // 轮询配置信息（用于调试）
            params.put("polling_interval", config.pollingInterval);
            params.put("trigger_mode", config.triggerMode);

            log.info("轮询触发策略执行: strategy={}, device={}.{}, value={} -> {}",
                strategyCode, config.deviceCode, config.attrKey, lastValue, currentValue);

            getStrategyExecutor().executeStrategy(strategyCode, params);

        } catch (Exception e) {
            log.error("策略[{}]轮询触发执行失败", strategyCode, e);
        }
    }

    // ==================== 配置解析 ====================

    /**
     * 解析轮询配置
     * 支持的配置格式（在 conditionExpr 中）：
     * {
     * "polling": {
     * "enabled": true,
     * "interval": 2000,
     * "activeQuery": true,
     * "queryWaitTime": 500,
     * "triggerMode": "ON_FIRST_MATCH",
     * "initialDelay": 1000,
     * "queryAbility": {
     * "abilityKey": "syncState",
     * "abilityParam": ""
     * }
     * },
     * "left": "Switch",
     * "op": "==",
     * "right": "1"
     * }
     */
    private PollingConfig parsePollingConfig(OpEnergyStrategyTrigger trigger) {
        PollingConfig config = new PollingConfig();

        config.strategyCode = trigger.getStrategyCode();
        config.deviceCode = trigger.getSourceObjCode();
        config.modelCode = trigger.getSourceModelCode();
        config.attrKey = trigger.getAttrKey();
        config.conditionExpr = trigger.getConditionExpr();

        // 解析条件表达式中的轮询配置
        if (trigger.getConditionExpr() != null && !trigger.getConditionExpr().isEmpty()) {
            try {
                JSONObject conditionJson = JSON.parseObject(trigger.getConditionExpr());

                // 检查是否有 polling 配置
                JSONObject pollingJson = conditionJson.getJSONObject("polling");
                if (pollingJson != null) {
                    config.pollingEnabled = pollingJson.getBooleanValue("enabled");
                    config.pollingInterval = pollingJson.getIntValue("interval");
                    if (config.pollingInterval <= 0) {
                        config.pollingInterval = 2000; // 默认2秒
                    }
                    config.activeQuery = pollingJson.getBooleanValue("activeQuery");
                    config.queryWaitTime = pollingJson.getIntValue("queryWaitTime");
                    if (config.queryWaitTime <= 0) {
                        config.queryWaitTime = 500; // 默认500ms
                    }
                    config.triggerMode = pollingJson.getString("triggerMode");
                    if (config.triggerMode == null) {
                        config.triggerMode = "ON_FIRST_MATCH";
                    }
                    config.initialDelay = pollingJson.getIntValue("initialDelay");
                    if (config.initialDelay <= 0) {
                        config.initialDelay = 1000; // 默认1秒后开始
                    }

                    // 解析查询能力配置
                    JSONObject queryAbilityJson = pollingJson.getJSONObject("queryAbility");
                    if (queryAbilityJson != null) {
                        config.queryAbilityKey = queryAbilityJson.getString("abilityKey");
                        config.queryAbilityParam = queryAbilityJson.getString("abilityParam");
                    }
                    else {
                        // 兼容旧配置：直接使用 abilityKey
                        config.queryAbilityKey = pollingJson.getString("queryAbilityKey");
                        config.queryAbilityParam = pollingJson.getString("queryAbilityParam");
                    }

                    // 如果开启了主动查询但没有配置能力，使用默认的 syncState
                    if (config.activeQuery && (config.queryAbilityKey == null || config.queryAbilityKey.isEmpty())) {
                        config.queryAbilityKey = "syncState";
                        config.queryAbilityParam = "";
                        log.info("策略[{}]未配置查询能力，使用默认能力: syncState", config.strategyCode);
                    }

                }
                else {
                    // 兼容旧格式：如果没有 polling 配置，使用默认值
                    config.pollingEnabled = true;
                    config.pollingInterval = 2000;
                    config.activeQuery = false;
                    config.queryWaitTime = 500;
                    config.triggerMode = "ON_FIRST_MATCH";
                    config.initialDelay = 1000;
                }

            }
            catch (Exception e) {
                log.warn("解析轮询配置失败: {}", trigger.getConditionExpr(), e);
                // 使用默认配置
                config.pollingEnabled = true;
                config.pollingInterval = 2000;
                config.triggerMode = "ON_FIRST_MATCH";
            }
        }

        return config;
    }

    // ==================== 统计方法 ====================

    /**
     * 获取已注册的轮询任务数量
     */
    public int getPollingTaskCount() {
        return pollingTasks.size();
    }

    /**
     * 获取轮询任务详情
     */
    public Map<String, Object> getPollingStatus() {
        Map<String, Object> status = new HashMap<>();
        status.put("taskCount", pollingTasks.size());
        status.put("registeredStrategies", pollingConfigs.keySet());

        Map<String, Object> configDetails = new HashMap<>();
        for (Map.Entry<String, PollingConfig> entry : pollingConfigs.entrySet()) {
            PollingConfig config = entry.getValue();
            Map<String, Object> detail = new HashMap<>();
            detail.put("deviceCode", config.deviceCode);
            detail.put("modelCode", config.modelCode);
            detail.put("attrKey", config.attrKey);
            detail.put("interval", config.pollingInterval);
            detail.put("triggerMode", config.triggerMode);
            detail.put("activeQuery", config.activeQuery);
            detail.put("queryAbilityKey", config.queryAbilityKey);
            detail.put("queryAbilityParam", config.queryAbilityParam);
            configDetails.put(entry.getKey(), detail);
        }
        status.put("configs", configDetails);

        return status;
    }

    /**
     * 检查某策略是否有轮询任务
     */
    public boolean hasPollingTask(String strategyCode) {
        return pollingTasks.containsKey(strategyCode);
    }

    /**
     * 手动触发一次轮询检查（用于测试）
     */
    public void triggerPollingCheck(String strategyCode) {
        PollingConfig config = pollingConfigs.get(strategyCode);
        if (config != null) {
            log.info("手动触发轮询检查: strategy={}", strategyCode);
            executePollingCheck(strategyCode, config);
        }
        else {
            log.warn("策略[{}]未注册轮询监控", strategyCode);
        }
    }

    // ==================== 内部类 ====================

    /**
     * 轮询配置
     */
    private static class PollingConfig {
        String strategyCode;      // 策略代码

        String deviceCode;        // 设备代码

        String modelCode;         // 模型代码

        String attrKey;           // 属性键

        String conditionExpr;     // 条件表达式

        boolean pollingEnabled;   // 是否启用轮询

        int pollingInterval;      // 轮询间隔（毫秒）

        int initialDelay;         // 初始延迟（毫秒）

        boolean activeQuery;      // 是否主动下发查询指令

        int queryWaitTime;        // 查询等待时间（毫秒）

        /**
         * 查询能力配置
         * 复用物模型中定义的能力（如 syncState）
         * 与界面上"能力调用"步骤使用相同的机制
         */
        String queryAbilityKey;   // 查询能力键（如 syncState）

        String queryAbilityParam; // 查询能力参数

        /**
         * 触发模式：
         * - ALWAYS: 只要条件满足就触发
         * - ON_CHANGE: 条件满足且值变化才触发
         * - ON_FIRST_MATCH: 条件满足且上次不满足才触发（边沿触发）
         */
        String triggerMode;
    }
}
