package com.ruoyi.ems.strategy.executor;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.ems.domain.OpEnergyStrategy;
import com.ruoyi.ems.domain.OpEnergyStrategyExecLog;
import com.ruoyi.ems.domain.OpEnergyStrategyStep;
import com.ruoyi.ems.domain.OpEnergyStrategyStepLog;
import com.ruoyi.ems.domain.StepExecutionResult;
import com.ruoyi.ems.domain.StrategyExecutionContext;
import com.ruoyi.ems.service.IOpEnergyStrategyExecLogService;
import com.ruoyi.ems.service.IOpEnergyStrategyService;
import com.ruoyi.ems.service.IOpEnergyStrategyStepLogService;
import com.ruoyi.ems.service.IOpEnergyStrategyStepService;
import com.ruoyi.ems.service.IStepExecutor;
import com.ruoyi.ems.strategy.evaluator.ConditionEvaluator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 策略执行器
 */
@Slf4j
@Component
public class StrategyExecutor {

    @Autowired
    private IOpEnergyStrategyService strategyService;

    @Autowired
    private IOpEnergyStrategyStepService stepService;

    @Autowired
    private IOpEnergyStrategyExecLogService execLogService;

    @Autowired
    private IOpEnergyStrategyStepLogService stepLogService;

    @Autowired
    private ConditionEvaluator conditionEvaluator;

    @Autowired
    private StepExecutorFactory stepExecutorFactory;

    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    /**
     * 执行策略
     *
     * @param strategyCode 策略代码
     * @param triggerData 触发数据，应包含：
     *                    - trigger_type: 触发类型 (MANUAL/TIME/EVENT/ATTR/POLLING)
     *                    - trigger_source: 触发源 (如: D-B-QS-10000001.Switch)
     *                    - exec_by: 执行人 (可选)
     * @return 执行ID
     */
    public String executeStrategy(String strategyCode, Map<String, Object> triggerData) {
        String execId = UUID.randomUUID().toString();

        // 查询策略
        OpEnergyStrategy strategy = strategyService.selectStrategyByCode(strategyCode);
        if (strategy == null) {
            log.error("策略不存在: {}", strategyCode);
            return null;
        }

        if (strategy.getStrategyState() != 1) {
            log.warn("策略未启用: {}", strategyCode);
            return null;
        }

        // 创建执行上下文
        StrategyExecutionContext context = new StrategyExecutionContext();
        context.setExecId(execId);
        context.setStrategyCode(strategyCode);
        context.setStartTime(System.currentTimeMillis());

        // ✅ 从 triggerData 中提取触发信息
        if (triggerData != null) {
            context.getVariables().putAll(triggerData);

            // 显式设置触发类型
            if (triggerData.containsKey("trigger_type")) {
                context.setTriggerType(String.valueOf(triggerData.get("trigger_type")));
            }

            // 显式设置触发源
            if (triggerData.containsKey("trigger_source")) {
                context.setTriggerSource(String.valueOf(triggerData.get("trigger_source")));
            }

            // 显式设置执行人
            if (triggerData.containsKey("exec_by")) {
                context.setExecBy(String.valueOf(triggerData.get("exec_by")));
            }
        }

        // ✅ 如果是手动触发且没有指定执行人，尝试获取当前登录用户
        if ("MANUAL".equals(context.getTriggerType()) && context.getExecBy() == null) {
            try {
                String username = SecurityUtils.getUsername();
                if (username != null) {
                    context.setExecBy(username);
                }
            } catch (Exception e) {
                // 如果无法获取用户信息（如在定时任务中），使用默认值
                log.debug("无法获取当前用户信息: {}", e.getMessage());
            }
        }

        // ✅ 设置默认触发源（如果未指定）
        if (context.getTriggerSource() == null || context.getTriggerSource().isEmpty()) {
            context.setTriggerSource(buildDefaultTriggerSource(context.getTriggerType(), strategy));
        }

        // 记录执行日志
        OpEnergyStrategyExecLog execLog = new OpEnergyStrategyExecLog();
        execLog.setExecId(execId);
        execLog.setStrategyCode(strategyCode);
        execLog.setTriggerType(context.getTriggerType());
        execLog.setTriggerSource(context.getTriggerSource());
        execLog.setExecBy(context.getExecBy());
        execLog.setExecStatus(0); // 执行中
        execLog.setStartTime(new Date());
        execLog.setContextData(JSON.toJSONString(context.getVariables()));

        log.info("策略[{}]开始执行: execId={}, triggerType={}, triggerSource={}, execBy={}",
            strategyCode, execId, execLog.getTriggerType(), execLog.getTriggerSource(), execLog.getExecBy());

        execLogService.insertExecLog(execLog);

        // 异步执行策略
        executorService.submit(() -> {
            try {
                executeStrategySteps(strategy, context, execLog);
            } catch (Exception e) {
                log.error("策略执行异常: {}", strategyCode, e);
                updateExecLogFailed(execLog, e.getMessage());
            }
        });

        return execId;
    }

    /**
     * 构建默认触发源
     */
    private String buildDefaultTriggerSource(String triggerType, OpEnergyStrategy strategy) {
        if (triggerType == null) {
            return "UNKNOWN";
        }

        switch (triggerType) {
            case "MANUAL":
                return "USER";
            case "TIME":
                return "SCHEDULER";
            case "EVENT":
                return "EVENT_LISTENER";
            case "ATTR":
                return "ATTR_CHANGE";
            case "POLLING":
                return "POLLING_MONITOR";
            default:
                return triggerType;
        }
    }

    private void executeStrategySteps(OpEnergyStrategy strategy,
        StrategyExecutionContext context,
        OpEnergyStrategyExecLog execLog) {

        List<OpEnergyStrategyStep> steps = stepService.selectStepsByStrategyCode(strategy.getStrategyCode());

        // 按步骤顺序排序，只取顶层步骤（parentStepCode 为空）
        steps = steps.stream()
            .filter(s -> s.getParentStepCode() == null || s.getParentStepCode().isEmpty())
            .sorted(Comparator.comparingInt(OpEnergyStrategyStep::getStepIndex))
            .collect(Collectors.toList());

        boolean allSuccess = true;

        for (OpEnergyStrategyStep step : steps) {
            if (step.getEnable() != 1) {
                continue;
            }

            // 评估执行条件
            if (step.getConditionExpr() != null && !step.getConditionExpr().isEmpty()) {
                boolean conditionMet = conditionEvaluator.evaluate(
                    step.getConditionExpr(),
                    context.getVariables()
                );
                if (!conditionMet) {
                    log.info("步骤条件不满足，跳过: {}", step.getStepCode());
                    recordStepSkipped(context.getExecId(), step);
                    continue;
                }
            }

            // 执行步骤
            StepExecutionResult result = executeStep(step, context);

            // ✅ 修复：增加空判断
            if (result == null) {
                log.error("步骤[{}]执行返回null，视为失败", step.getStepCode());
                allSuccess = false;
                if (step.getContinueOnFail() != 1) {
                    log.error("步骤执行失败且未配置失败继续，终止策略: {}", step.getStepCode());
                    break;
                }
                continue;
            }

            if (!result.isSuccess()) {
                allSuccess = false;
                if (step.getContinueOnFail() != 1) {
                    log.error("步骤执行失败，终止策略: {}", step.getStepCode());
                    break;
                }
            }

            // 保存步骤结果到上下文
            context.setStepResult(step.getStepCode(), result.getResult());
        }

        // 更新执行日志
        long duration = System.currentTimeMillis() - context.getStartTime();
        execLog.setEndTime(new Date());
        execLog.setDuration((int) duration);
        execLog.setExecStatus(allSuccess ? 1 : 2);
        execLog.setResultData(JSON.toJSONString(context.getStepResults()));
        execLogService.updateExecLog(execLog);

        // 更新策略统计
        updateStrategyStats(strategy, allSuccess);

        log.info("策略[{}]执行完成: execId={}, 耗时={}ms, 结果={}",
            strategy.getStrategyCode(), context.getExecId(), duration, allSuccess ? "成功" : "失败");
    }

    private StepExecutionResult executeStep(OpEnergyStrategyStep step,
        StrategyExecutionContext context) {
        long startTime = System.currentTimeMillis();

        // 记录步骤开始
        OpEnergyStrategyStepLog stepLog = new OpEnergyStrategyStepLog();
        stepLog.setExecId(context.getExecId());
        stepLog.setStrategyCode(step.getStrategyCode());
        stepLog.setStepCode(step.getStepCode());
        stepLog.setStepName(step.getStepName());
        stepLog.setStepIndex(step.getStepIndex());
        stepLog.setExecStatus(0);
        stepLog.setStartTime(new Date());

        // ✅ 记录输入参数
        stepLog.setInputParam(buildStepInputParam(step, context));

        stepLogService.insertStepLog(stepLog);

        try {
            // 根据步骤类型获取执行器
            IStepExecutor executor = stepExecutorFactory.getExecutor(step.getStepType());

            // 执行步骤
            Object result = executor.execute(step, context);

            long duration = System.currentTimeMillis() - startTime;

            // 更新步骤日志
            stepLog.setExecStatus(1);
            stepLog.setEndTime(new Date());
            stepLog.setDuration((int) duration);
            stepLog.setOutputResult(JSON.toJSONString(result));
            stepLogService.updateStepLog(stepLog);

            return StepExecutionResult.success(step.getStepCode(), result, duration);

        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("步骤执行失败: {}", step.getStepCode(), e);

            // 更新步骤日志
            stepLog.setExecStatus(2);
            stepLog.setEndTime(new Date());
            stepLog.setDuration((int) duration);
            stepLog.setErrorMessage(e.getMessage());
            stepLogService.updateStepLog(stepLog);

            return StepExecutionResult.fail(step.getStepCode(), e.getMessage(), duration);
        }
    }

    /**
     * 构建步骤输入参数（用于日志记录）
     */
    private String buildStepInputParam(OpEnergyStrategyStep step, StrategyExecutionContext context) {
        try {
            Map<String, Object> inputParam = new java.util.HashMap<>();
            inputParam.put("stepType", step.getStepType());

            if ("ABILITY".equals(step.getStepType())) {
                inputParam.put("targetObjCode", step.getTargetObjCode());
                inputParam.put("abilityKey", step.getAbilityKey());
                inputParam.put("abilityParam", step.getAbilityParam());
                inputParam.put("paramSource", step.getParamSource());
            } else if ("DELAY".equals(step.getStepType())) {
                inputParam.put("delaySeconds", step.getDelaySeconds());
            } else if ("ATTR_QUERY".equals(step.getStepType())) {
                inputParam.put("targetObjCode", step.getTargetObjCode());
                inputParam.put("attrKey", step.getAbilityKey());
            } else if ("LOOP".equals(step.getStepType())) {
                inputParam.put("loopMaxCount", step.getLoopMaxCount());
                inputParam.put("loopInterval", step.getLoopInterval());
                inputParam.put("loopCondition", step.getLoopCondition());
            } else if ("CONDITION".equals(step.getStepType())) {
                inputParam.put("conditionExpr", step.getConditionExpr());
            }

            return JSON.toJSONString(inputParam);
        } catch (Exception e) {
            return "{}";
        }
    }

    private void recordStepSkipped(String execId, OpEnergyStrategyStep step) {
        OpEnergyStrategyStepLog stepLog = new OpEnergyStrategyStepLog();
        stepLog.setExecId(execId);
        stepLog.setStrategyCode(step.getStrategyCode());
        stepLog.setStepCode(step.getStepCode());
        stepLog.setStepName(step.getStepName());
        stepLog.setStepIndex(step.getStepIndex());
        stepLog.setExecStatus(3); // 跳过
        stepLog.setStartTime(new Date());
        stepLog.setEndTime(new Date());
        stepLog.setDuration(0);
        stepLogService.insertStepLog(stepLog);
    }

    private void updateExecLogFailed(OpEnergyStrategyExecLog execLog, String error) {
        execLog.setExecStatus(2);
        execLog.setEndTime(new Date());
        execLog.setErrorMessage(error);
        execLogService.updateExecLog(execLog);
    }

    private void updateStrategyStats(OpEnergyStrategy strategy, boolean success) {
        strategy.setExecCount((strategy.getExecCount() != null ? strategy.getExecCount() : 0) + 1);
        if (success) {
            strategy.setSuccessCount((strategy.getSuccessCount() != null ? strategy.getSuccessCount() : 0) + 1);
            strategy.setLastExecResult(0);
        } else {
            strategy.setFailCount((strategy.getFailCount() != null ? strategy.getFailCount() : 0) + 1);
            strategy.setLastExecResult(1);
        }
        strategy.setLastExecTime(new Date());
        strategyService.updateStrategy(strategy);
    }
}