package com.ruoyi.ems.controller;

import com.alibaba.fastjson2.JSONObject;
import com.huashe.common.domain.AjaxResult;
import com.huashe.common.exception.BusinessException;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.web.page.TableDataInfo;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.ems.domain.OpEnergyStrategy;
import com.ruoyi.ems.domain.OpEnergyStrategyExecLog;
import com.ruoyi.ems.domain.OpEnergyStrategyParam;
import com.ruoyi.ems.domain.OpEnergyStrategyStep;
import com.ruoyi.ems.domain.OpEnergyStrategyStepLog;
import com.ruoyi.ems.domain.OpEnergyStrategyTemplate;
import com.ruoyi.ems.domain.OpEnergyStrategyTrigger;
import com.ruoyi.ems.service.IOpEnergyStrategyExecLogService;
import com.ruoyi.ems.service.IOpEnergyStrategyParamService;
import com.ruoyi.ems.service.IOpEnergyStrategyService;
import com.ruoyi.ems.service.IOpEnergyStrategyStepService;
import com.ruoyi.ems.service.IOpEnergyStrategyTemplateService;
import com.ruoyi.ems.service.IOpEnergyStrategyTriggerService;
import com.ruoyi.ems.strategy.PollingMonitorService;
import com.ruoyi.ems.strategy.executor.StrategyExecutor;
import com.ruoyi.ems.task.StrategyScheduler;
import com.ruoyi.ems.task.StrategyTriggerListener;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 能源策略Controller
 */
@Slf4j
@RestController
@RequestMapping("/energyStrategy")
@Api(value = "OpEnergyStrategyController", description = "能源策略管理接口")
public class OpEnergyStrategyController extends BaseController {

    @Autowired
    private IOpEnergyStrategyService strategyService;

    @Autowired
    private IOpEnergyStrategyTemplateService templateService;

    @Autowired
    private IOpEnergyStrategyParamService paramService;

    @Autowired
    private IOpEnergyStrategyStepService stepService;

    @Autowired
    private IOpEnergyStrategyTriggerService triggerService;

    @Autowired
    private IOpEnergyStrategyExecLogService execLogService;

    @Autowired
    private StrategyExecutor strategyExecutor;

    // 策略调度器
    @Autowired
    private StrategyScheduler strategyScheduler;

    // 触发监听器
    @Autowired
    private StrategyTriggerListener triggerListener;

    // 轮询监控服务
    @Autowired
    private PollingMonitorService pollingMonitorService;

    // ==================== 策略基础管理 ====================

    @RequiresPermissions("power-mgr:strategy:list")
    @GetMapping("/list")
    public TableDataInfo list(OpEnergyStrategy strategy) {
        startPage();
        List<OpEnergyStrategy> list = strategyService.selectStrategyList(strategy);
        return getDataTable(list);
    }

    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(strategyService.selectStrategyById(id));
    }

    @GetMapping(value = "/code/{strategyCode}")
    public AjaxResult getByCode(@PathVariable("strategyCode") String strategyCode) {
        return success(strategyService.selectStrategyByCode(strategyCode));
    }

    @RequiresPermissions("power-mgr:strategy:add")
    @Log(title = "能源策略", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody OpEnergyStrategy strategy) {
        return toAjax(strategyService.insertStrategy(strategy));
    }

    /**
     * 修改能源策略
     * 重构：修改后刷新调度器
     */
    @RequiresPermissions("power-mgr:strategy:edit")
    @Log(title = "能源策略", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody OpEnergyStrategy strategy) {
        int result = strategyService.updateStrategy(strategy);

        if (result > 0 && strategy.getStrategyState() != null && strategy.getStrategyState() == 1) {
            // 刷新调度器中的策略配置
            strategyScheduler.refreshStrategy(strategy.getStrategyCode());
            log.info("策略[{}]已更新并刷新调度器", strategy.getStrategyCode());
        }

        return toAjax(result);
    }

    /**
     * 删除能源策略
     * 删除前从调度器注销
     */
    @RequiresPermissions("power-mgr:strategy:remove")
    @Log(title = "能源策略", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        // 先从调度器注销
        for (Long id : ids) {
            OpEnergyStrategy strategy = strategyService.selectStrategyById(id);
            if (strategy != null) {
                strategyScheduler.unregisterStrategy(strategy.getStrategyCode());
                // 【新增】同时注销轮询监控
                pollingMonitorService.unregisterPollingStrategy(strategy.getStrategyCode());
                log.info("策略[{}]已从调度器注销", strategy.getStrategyCode());
            }
        }

        return toAjax(strategyService.deleteStrategyByIds(ids));
    }

    /**
     * 启用/停用策略
     * 重构：统一通过 StrategyScheduler 管理触发器注册
     */
    @PutMapping("/state/{strategyCode}/{state}")
    @ApiOperation("启用/停用策略")
    public AjaxResult changeState(@PathVariable String strategyCode, @PathVariable Integer state) {
        OpEnergyStrategy strategy = strategyService.selectStrategyByCode(strategyCode);
        if (strategy == null) {
            return error("策略不存在");
        }

        strategy.setStrategyState(state);
        int result = strategyService.updateStrategy(strategy);

        if (result > 0) {
            if (state == 1) {
                // 启用策略 - 由调度器统一处理所有触发器类型
                strategyScheduler.registerStrategy(strategy);
                log.info("策略[{}]已启用并注册到调度器", strategyCode);
            }
            else {
                // 停用策略 - 注销所有触发器
                strategyScheduler.unregisterStrategy(strategyCode);
                log.info("策略[{}]已停用并从调度器注销", strategyCode);
            }
        }

        return toAjax(result);
    }

    @GetMapping("/sceneCount")
    public AjaxResult getSceneTypeCount(@RequestParam(required = false) String areaCode) {
        return success(strategyService.getSceneTypeCount(areaCode));
    }

    // ==================== 属性变化通知接口（供采集模块调用） ====================

    /**
     * 属性值变化通知接口
     * 由 ems-dev-adapter 采集模块调用
     *
     * @param objCode  设备代码
     * @param attrKey  属性键
     * @param oldValue 旧值（可选）
     * @param newValue 新值
     * @return 触发的策略数量
     */
    @PostMapping("/onAttrValueChanged")
    @ApiOperation("属性值变化通知（供采集模块调用）")
    public AjaxResult onAttrValueChanged(@ApiParam("设备代码") @RequestParam String objCode,
        @ApiParam("属性键") @RequestParam String attrKey,
        @ApiParam("旧值") @RequestParam(required = false) String oldValue,
        @ApiParam("新值") @RequestParam String newValue) {

        log.debug("收到属性变化通知: obj={}, attr={}, {} -> {}", objCode, attrKey, oldValue, newValue);

        int triggeredCount = triggerListener.handleAttrChange(objCode, attrKey, oldValue, newValue);

        Map<String, Object> result = new HashMap<>();
        result.put("objCode", objCode);
        result.put("attrKey", attrKey);
        result.put("triggeredStrategies", triggeredCount);

        return success(result);
    }

    /**
     * 批量属性变化通知接口
     *
     * @param request 包含 objCode 和 attributes 的请求体
     */
    @PostMapping("/onAttrValueChangedBatch")
    @ApiOperation("批量属性值变化通知（供采集模块调用）")
    public AjaxResult onAttrValueChangedBatch(@RequestBody Map<String, Object> request) {
        String objCode = (String) request.get("objCode");
        @SuppressWarnings("unchecked") Map<String, Object> attributes = (Map<String, Object>) request.get("attributes");

        if (objCode == null || attributes == null) {
            return error("参数不完整，需要 objCode 和 attributes");
        }

        log.debug("收到批量属性变化通知: obj={}, attrCount={}", objCode, attributes.size());

        int totalTriggered = 0;
        for (Map.Entry<String, Object> entry : attributes.entrySet()) {
            String attrKey = entry.getKey();
            Object newValue = entry.getValue();

            int triggered = triggerListener.handleAttrChange(objCode, attrKey, null, newValue);
            totalTriggered += triggered;
        }

        Map<String, Object> result = new HashMap<>();
        result.put("objCode", objCode);
        result.put("attributeCount", attributes.size());
        result.put("triggeredStrategies", totalTriggered);

        return success(result);
    }

    /**
     * 设备事件通知接口
     */
    @PostMapping("/onDeviceEvent")
    @ApiOperation("设备事件通知（供采集模块调用）")
    public AjaxResult onDeviceEvent(@ApiParam("设备代码") @RequestParam String objCode,
        @ApiParam("事件键") @RequestParam String eventKey,
        @RequestBody(required = false) Map<String, Object> eventData) {

        log.debug("收到设备事件通知: obj={}, event={}", objCode, eventKey);

        if (eventData == null) {
            eventData = new HashMap<>();
        }

        int triggeredCount = triggerListener.handleDeviceEvent(objCode, eventKey, eventData);

        Map<String, Object> result = new HashMap<>();
        result.put("objCode", objCode);
        result.put("eventKey", eventKey);
        result.put("triggeredStrategies", triggeredCount);

        return success(result);
    }

    // ==================== 调度器状态接口 ====================

    /**
     * 获取调度器状态
     * 新增：更详细的状态信息
     */
    @GetMapping("/scheduler/status")
    @ApiOperation("获取调度器状态")
    public AjaxResult getSchedulerStatus() {
        Map<String, Object> status = strategyScheduler.getStatus();

        // 添加触发监听器状态
        if (triggerListener != null) {
            status.put("triggers", triggerListener.getRegisteredTriggers());
        }

        // 添加轮询监控状态
        if (pollingMonitorService != null) {
            status.put("polling", pollingMonitorService.getPollingStatus());
        }

        return success(status);
    }


    /**
     * 重新加载所有策略
     */
    @PostMapping("/scheduler/reload")
    @ApiOperation("重新加载所有策略")
    public AjaxResult reloadScheduler() {
        strategyScheduler.loadEnabledStrategies();
        return success("调度器已重新加载");
    }

    // ==================== 轮询监控接口 ====================

    /**
     * 获取轮询监控状态
     */
    @GetMapping("/polling/status")
    @ApiOperation("获取轮询监控状态")
    public AjaxResult getPollingStatus() {
        return success(pollingMonitorService.getPollingStatus());
    }

    /**
     * 刷新指定策略的轮询配置
     */
    @PostMapping("/polling/refresh/{strategyCode}")
    @ApiOperation("刷新策略轮询配置")
    public AjaxResult refreshPollingStrategy(@PathVariable String strategyCode) {
        pollingMonitorService.refreshPollingStrategy(strategyCode);
        return success("轮询配置已刷新");
    }

    /**
     * 手动触发一次轮询检查（用于调试）
     * 【改造点】：完善实现，调用 PollingMonitorService.triggerPollingCheck()
     */
    @PostMapping("/polling/trigger/{strategyCode}")
    @ApiOperation("手动触发轮询检查")
    public AjaxResult manualTriggerPolling(@PathVariable String strategyCode) {
        if (!pollingMonitorService.hasPollingTask(strategyCode)) {
            return error("该策略未配置轮询监控或未启用");
        }

        try {
            // 【改造】调用轮询服务的手动触发方法
            pollingMonitorService.triggerPollingCheck(strategyCode);

            Map<String, Object> result = new HashMap<>();
            result.put("strategyCode", strategyCode);
            result.put("message", "已触发轮询检查，请查看日志确认执行结果");

            return success(result);
        }
        catch (Exception e) {
            log.error("手动触发轮询检查失败: strategy={}", strategyCode, e);
            return error("触发轮询检查失败: " + e.getMessage());
        }
    }

    /**
     * 【新增】检查策略是否有轮询任务
     */
    @GetMapping("/polling/check/{strategyCode}")
    @ApiOperation("检查策略是否有轮询任务")
    public AjaxResult checkPollingTask(@PathVariable String strategyCode) {
        boolean hasTask = pollingMonitorService.hasPollingTask(strategyCode);

        Map<String, Object> result = new HashMap<>();
        result.put("strategyCode", strategyCode);
        result.put("hasPollingTask", hasTask);
        result.put("message", hasTask ? "已注册轮询监控" : "未注册轮询监控");

        return success(result);
    }

    // ==================== 策略参数管理 ====================

    @GetMapping(value = "/param")
    public AjaxResult getStrategyParam(@RequestParam(name = "strategyCode") String strategyCode) {
        List<OpEnergyStrategyParam> paramList = paramService.selectParamByStrategyCode(strategyCode);
        return success(buildStrategyParams(paramList));
    }

    @RequiresPermissions("power-mgr:strategy:edit")
    @Log(title = "能源策略参数", businessType = BusinessType.UPDATE)
    @PutMapping("/param")
    public AjaxResult editParam(@RequestBody OpEnergyStrategyParam strategyParam) {
        try {
            int updateCnt = paramService.updateParamValue(strategyParam);
            return toAjax(updateCnt);
        }
        catch (BusinessException e) {
            return error(e.getMessage());
        }
    }

    private Map<String, Map<String, JSONObject>> buildStrategyParams(List<OpEnergyStrategyParam> paramList) {
        Map<String, Map<String, JSONObject>> params = new HashMap<>();
        Map<String, List<OpEnergyStrategyParam>> groupedMap = paramList.stream()
            .collect(Collectors.groupingBy(OpEnergyStrategyParam::getParamGroup, Collectors.toList()));

        for (Map.Entry<String, List<OpEnergyStrategyParam>> entry : groupedMap.entrySet()) {
            String groupName = entry.getKey();
            List<OpEnergyStrategyParam> groupParams = entry.getValue();
            Map<String, JSONObject> groupParamMap = new HashMap<>();
            for (OpEnergyStrategyParam param : groupParams) {
                JSONObject option = new JSONObject();
                option.put("paramName", param.getParamName());
                option.put("paramValue", param.getParamValue());
                option.put("editEnable", param.getEditEnable());
                option.put("paramValueFormat", param.getParamValueFormat());
                groupParamMap.put(param.getParamKey(), option);
            }
            params.put(groupName, groupParamMap);
        }
        return params;
    }

    // ==================== 策略步骤管理 ====================

    @GetMapping(value = "/step")
    public AjaxResult getStrategyStep(@RequestParam(name = "strategyCode") String strategyCode) {
        return success(stepService.selectStepByStrategyCode(strategyCode));
    }

    @PostMapping(value = "/step")
    public AjaxResult addStrategyStep(@RequestBody OpEnergyStrategyStep strategyStep) {
        return toAjax(stepService.insertStep(strategyStep));
    }

    @PutMapping(value = "/step")
    public AjaxResult editStrategyStep(@RequestBody OpEnergyStrategyStep strategyStep) {
        return toAjax(stepService.updateStep(strategyStep));
    }

    @DeleteMapping(value = "/step/{id}")
    public AjaxResult delStrategyStep(@PathVariable Long id) {
        return toAjax(stepService.deleteStep(id));
    }

    @PutMapping(value = "/step/batch")
    public AjaxResult editStrategyStepBatch(@RequestBody List<OpEnergyStrategyStep> strategySteps) {
        if (CollectionUtils.isEmpty(strategySteps)) {
            return error("步骤列表不能为空");
        }
        stepService.deleteStepByStrategyCode(strategySteps.get(0).getStrategyCode());
        return toAjax(stepService.insertStepBatch(strategySteps));
    }

    // ==================== 触发器管理 ====================

    @GetMapping("/trigger/{strategyCode}")
    @ApiOperation("获取触发器列表")
    public AjaxResult getTriggers(@PathVariable String strategyCode) {
        List<OpEnergyStrategyTrigger> triggers = triggerService.selectByStrategyCode(strategyCode);
        return success(triggers);
    }

    /**
     * 保存触发器
     *
     * @param trigger
     * @return
     */
    @PostMapping("/trigger")
    @ApiOperation("保存触发器")
    public AjaxResult saveTrigger(@RequestBody OpEnergyStrategyTrigger trigger) {
        // 确保 strategyCode 存在
        if (trigger.getStrategyCode() == null || trigger.getStrategyCode().isEmpty()) {
            return error("策略代码不能为空");
        }

        int result;
        if (trigger.getId() == null) {
            result = triggerService.insertTrigger(trigger);
        }
        else {
            result = triggerService.updateTrigger(trigger);
        }

        if (result > 0) {
            // 同步更新策略的触发类型（用于列表筛选）
            syncStrategyTriggerType(trigger.getStrategyCode());

            // 检查策略是否已启用，如果是则刷新调度器
            OpEnergyStrategy strategy = strategyService.selectStrategyByCode(trigger.getStrategyCode());
            if (strategy != null && strategy.getStrategyState() == 1) {
                strategyScheduler.refreshStrategy(trigger.getStrategyCode());
                log.info("触发器保存后刷新调度器: strategy={}", trigger.getStrategyCode());
            }
        }

        return toAjax(result);
    }

    /**
     * 删除触发器
     */
    @DeleteMapping("/trigger/{id}")
    @ApiOperation("删除触发器")
    public AjaxResult deleteTrigger(@PathVariable Long id) {
        // 先查询触发器获取策略代码
        OpEnergyStrategyTrigger trigger = triggerService.selectById(id);
        String strategyCode = trigger != null ? trigger.getStrategyCode() : null;

        int result = triggerService.deleteTrigger(id);

        if (result > 0 && strategyCode != null) {
            // 检查策略是否已启用，如果是则刷新调度器
            OpEnergyStrategy strategy = strategyService.selectStrategyByCode(strategyCode);
            if (strategy != null && strategy.getStrategyState() == 1) {
                strategyScheduler.refreshStrategy(strategyCode);
            }
        }

        return toAjax(result);
    }

    // ==================== 模板管理 ====================

    @GetMapping("/template/list")
    public AjaxResult listTemplate(OpEnergyStrategyTemplate template) {
        List<OpEnergyStrategyTemplate> list = templateService.selectTemplateList(template);
        return success(list);
    }

    // ==================== 策略执行 ====================

    @PostMapping("/execute/{strategyCode}")
    @ApiOperation("手动执行策略")
    public AjaxResult executeStrategy(@PathVariable String strategyCode,
        @RequestBody(required = false) Map<String, Object> params) {
        try {
            if (params == null) {
                params = new HashMap<>();
            }

            // 设置触发类型和触发源
            params.put("trigger_type", "MANUAL");
            params.put("trigger_source", "USER_MANUAL");

            // 设置执行人（尝试获取当前登录用户）
            try {
                String username = SecurityUtils.getUsername();
                if (username != null && !username.isEmpty()) {
                    params.put("exec_by", username);
                }
                else {
                    params.put("exec_by", "UNKNOWN_USER");
                }
            }
            catch (Exception e) {
                params.put("exec_by", "ANONYMOUS");
                log.debug("无法获取当前用户: {}", e.getMessage());
            }

            String execId = strategyExecutor.executeStrategy(strategyCode, params);

            Map<String, Object> result = new HashMap<>();
            result.put("execId", execId);
            result.put("message", "策略执行已启动");
            result.put("triggerType", "MANUAL");
            result.put("execBy", params.get("exec_by"));

            return success(result);
        }
        catch (Exception e) {
            log.error("手动执行策略失败: {}", strategyCode, e);
            return error("策略执行失败: " + e.getMessage());
        }
    }

    // ==================== 执行日志 ====================

    @GetMapping("/execLog/list")
    @ApiOperation("获取执行日志列表")
    public TableDataInfo getExecLogList(OpEnergyStrategyExecLog param) {
        startPage();
        List<OpEnergyStrategyExecLog> execLogs = execLogService.selectExecLogList(param);
        return getDataTable(execLogs);
    }

    @GetMapping("/execLog/{execId}")
    @ApiOperation("获取执行日志详情")
    public AjaxResult getExecLog(@PathVariable String execId) {
        OpEnergyStrategyExecLog execLog = execLogService.selectByExecId(execId);
        if (execLog == null) {
            return error("执行日志不存在");
        }
        List<OpEnergyStrategyStepLog> stepLogs = execLogService.selectStepLogsByExecId(execId);
        Map<String, Object> result = new HashMap<>();
        result.put("execLog", execLog);
        result.put("stepLogs", stepLogs);
        return success(result);
    }

    @GetMapping("/execLog/steps/{execId}")
    @ApiOperation("获取步骤执行日志")
    public AjaxResult getStepExecLog(@PathVariable String execId) {
        List<OpEnergyStrategyStepLog> stepLogs = execLogService.selectStepLogsByExecId(execId);
        return success(stepLogs);
    }

    /**
     * 保存触发器后同步更新策略的触发类型
     */
    private void syncStrategyTriggerType(String strategyCode) {
        List<OpEnergyStrategyTrigger> triggers = triggerService.selectByStrategyCode(strategyCode);
        if (triggers.isEmpty()) {
            return;
        }

        // 取第一个启用的触发器的类型
        OpEnergyStrategyTrigger firstTrigger = triggers.stream().filter(t -> t.getEnable() == 1).findFirst()
            .orElse(null);

        if (firstTrigger != null) {
            OpEnergyStrategy strategy = strategyService.selectStrategyByCode(strategyCode);
            if (strategy != null) {
                Integer triggerType = mapTriggerType(firstTrigger.getTriggerType());
                strategy.setTriggerType(triggerType);
                strategyService.updateStrategy(strategy);
            }
        }
    }

    /**
     * 触发器类型字符串转整数
     */
    private Integer mapTriggerType(String triggerType) {
        if (triggerType == null)
            return 3;
        switch (triggerType) {
            case "EVENT":
                return 1;
            case "TIME":
                return 2;
            case "ATTR":
                return 4;
            case "POLLING":
                return 5;
            default:
                return 3;
        }
    }
}