/*
 * 文 件 名:  AlarmRuleEngine
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  <描述>
 * 修 改 人:  lvwenbin
 * 修改时间:  2026/2/3
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */
package com.ruoyi.ems.service.alarm;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.ems.domain.Alarm;
import com.ruoyi.ems.domain.AlarmRule;
import com.ruoyi.ems.domain.EmsDevice;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 告警规则引擎
 *
 * 【核心功能】
 * 1. 规则匹配：判断设备属性值是否触发规则
 * 2. 告警生成：根据规则生成告警记录
 * 3. 恢复检测：检测告警是否已恢复
 * 4. 消息渲染：支持模板变量替换
 *
 * 【设计说明】
 * 此引擎可被多个场景复用：
 * - 实时数据监测
 * - 自动巡检
 * - 手动触发检查
 */
@Slf4j
@Component
public class AlarmRuleEngine {

    /** 模板变量正则 */
    private static final Pattern TEMPLATE_VAR_PATTERN = Pattern.compile("\\$\\{(\\w+)}");

    /**
     * 检查结果
     */
    @Data
    public static class CheckResult {
        /** 是否触发告警 */
        private boolean triggered;

        /** 是否恢复 */
        private boolean recovered;

        /** 检查的属性值 */
        private String attrValue;

        /** 告警消息 */
        private String message;

        /** 触发的规则 */
        private AlarmRule rule;

        /** 检查时间 */
        private Date checkTime;

        public static CheckResult normal(String attrValue) {
            CheckResult result = new CheckResult();
            result.setTriggered(false);
            result.setRecovered(false);
            result.setAttrValue(attrValue);
            result.setCheckTime(new Date());
            return result;
        }

        public static CheckResult triggered(AlarmRule rule, String attrValue, String message) {
            CheckResult result = new CheckResult();
            result.setTriggered(true);
            result.setRecovered(false);
            result.setRule(rule);
            result.setAttrValue(attrValue);
            result.setMessage(message);
            result.setCheckTime(new Date());
            return result;
        }

        public static CheckResult recovered(AlarmRule rule, String attrValue) {
            CheckResult result = new CheckResult();
            result.setTriggered(false);
            result.setRecovered(true);
            result.setRule(rule);
            result.setAttrValue(attrValue);
            result.setCheckTime(new Date());
            return result;
        }
    }

    /**
     * 检查单个规则
     *
     * @param rule 告警规则
     * @param device 设备信息
     * @param attrValue 属性当前值
     * @return 检查结果
     */
    public CheckResult checkRule(AlarmRule rule, EmsDevice device, String attrValue) {
        if (rule == null || rule.getCheckType() == null) {
            return CheckResult.normal(attrValue);
        }

        // 规则未启用
        if (rule.getEnabled() == null || rule.getEnabled() != 1) {
            return CheckResult.normal(attrValue);
        }

        // 检查设备模型是否匹配
        if (StringUtils.isNotBlank(rule.getDeviceModel())
            && !StringUtils.equals(rule.getDeviceModel(), device.getDeviceModel())) {
            return CheckResult.normal(attrValue);
        }

        // 检查目标范围
        if (!isTargetInScope(rule, device)) {
            return CheckResult.normal(attrValue);
        }

        boolean triggered = false;
        String message = null;

        try {
            switch (rule.getCheckType()) {
                case AlarmRule.CHECK_TYPE_RANGE:
                    triggered = checkRange(attrValue, rule.getThresholdMin(), rule.getThresholdMax(), rule.getOperator());
                    break;
                case AlarmRule.CHECK_TYPE_EQUAL:
                    triggered = checkEqual(attrValue, rule.getThresholdValue(), rule.getOperator());
                    break;
                case AlarmRule.CHECK_TYPE_NOT_NULL:
                    triggered = checkNotNull(attrValue, rule.getOperator());
                    break;
                case AlarmRule.CHECK_TYPE_ONLINE:
                    triggered = checkOnline(attrValue, rule.getThresholdValue());
                    break;
                case AlarmRule.CHECK_TYPE_CHANGE_RATE:
                    // 变化率检查需要历史值，暂不实现
                    triggered = false;
                    break;
                default:
                    log.warn("未知的检查类型: {}", rule.getCheckType());
            }
        } catch (Exception e) {
            log.error("规则检查异常: ruleCode={}, attrValue={}, error={}",
                rule.getRuleCode(), attrValue, e.getMessage());
            return CheckResult.normal(attrValue);
        }

        if (triggered) {
            message = renderMessage(rule, device, attrValue);
            return CheckResult.triggered(rule, attrValue, message);
        }

        // 检查是否恢复（如果规则启用了恢复检查）
        if (rule.getRecoveryCheck() != null && rule.getRecoveryCheck() == 1) {
            boolean recovered = checkRecovery(rule, attrValue);
            if (recovered) {
                return CheckResult.recovered(rule, attrValue);
            }
        }

        return CheckResult.normal(attrValue);
    }

    /**
     * 批量检查规则
     *
     * @param rules 规则列表
     * @param device 设备信息
     * @param attrValues 属性值Map
     * @return 触发的检查结果列表
     */
    public List<CheckResult> checkRules(List<AlarmRule> rules, EmsDevice device, Map<String, String> attrValues) {
        List<CheckResult> results = new ArrayList<>();

        if (rules == null || rules.isEmpty() || attrValues == null) {
            return results;
        }

        // 按优先级排序
        rules.sort(Comparator.comparingInt(r -> r.getRuleOrder() != null ? r.getRuleOrder() : 0));

        for (AlarmRule rule : rules) {
            String attrValue = attrValues.get(rule.getAttrKey());

            // 特殊处理设备状态
            if ("deviceStatus".equals(rule.getAttrKey()) && device.getDeviceStatus() != null) {
                attrValue = device.getDeviceStatus().toString();
            }

            CheckResult result = checkRule(rule, device, attrValue);
            if (result.isTriggered() || result.isRecovered()) {
                results.add(result);
            }
        }

        return results;
    }

    /**
     * 根据检查结果生成告警对象
     *
     * @param result 检查结果
     * @param device 设备信息
     * @param source 告警来源
     * @param sourceRef 来源引用
     * @return 告警对象
     */
    public Alarm createAlarm(CheckResult result, EmsDevice device, int source, String sourceRef) {
        if (!result.isTriggered() || result.getRule() == null) {
            return null;
        }

        AlarmRule rule = result.getRule();
        Date now = new Date();

        Alarm alarm = new Alarm();
        alarm.setAlarmId(generateAlarmId());
        alarm.setAreaCode(device.getAreaCode());
        alarm.setSubsystemCode(rule.getSubsystemCode());
        alarm.setTargetType(rule.getTargetType() != null ? rule.getTargetType() : 2);
        alarm.setTargetCode(device.getDeviceCode());
        alarm.setTargetName(device.getDeviceName());
        alarm.setDeviceModel(device.getDeviceModel());
        alarm.setDeviceModelName(device.getDeviceModelName());
        alarm.setLocation(device.getLocation());
        alarm.setRuleCode(rule.getRuleCode());
        alarm.setRuleName(rule.getRuleName());
        alarm.setAttrKey(rule.getAttrKey());
        alarm.setAttrName(rule.getAttrName());
        alarm.setAttrValue(result.getAttrValue());
        alarm.setThresholdValue(rule.getThresholdDisplay());
        alarm.setAlarmLevel(rule.getAlarmLevel() != null ? rule.getAlarmLevel() : Alarm.LEVEL_NORMAL);
        alarm.setAlarmCode(rule.getAlarmCode());
        alarm.setAlarmMsg(result.getMessage());
        alarm.setAlarmSource(source);
        alarm.setSourceRef(sourceRef);
        alarm.setAlarmTime(now);
        alarm.setAlarmDate(now);
        alarm.setAlarmStatus(Alarm.STATUS_ACTIVE);
        alarm.setRepeatCount(1);
        alarm.setLastOccurTime(now);
        alarm.setIsNotified(0);

        return alarm;
    }

    // ==================== 私有方法 ====================

    /**
     * 检查目标是否在规则范围内
     */
    private boolean isTargetInScope(AlarmRule rule, EmsDevice device) {
        // 如果没有限定范围，则匹配所有
        if (StringUtils.isBlank(rule.getTargetScope())) {
            return true;
        }

        try {
            List<String> scopeList = JSON.parseArray(rule.getTargetScope(), String.class);
            if (scopeList == null || scopeList.isEmpty()) {
                return true;
            }

            // 根据目标类型判断
            Integer targetType = rule.getTargetType();
            if (targetType == null) {
                targetType = 2; // 默认设备
            }

            switch (targetType) {
                case 0: // 区域
                    return scopeList.contains(device.getAreaCode());
                case 1: // 设施
                    return scopeList.contains(device.getRefFacs());
                case 2: // 设备
                    return scopeList.contains(device.getDeviceCode());
                default:
                    return true;
            }
        } catch (Exception e) {
            log.warn("解析目标范围失败: {}", rule.getTargetScope());
            return true;
        }
    }

    /**
     * 范围检查
     */
    private boolean checkRange(String value, String min, String max, String operator) {
        if (StringUtils.isBlank(value)) {
            return false;
        }

        try {
            BigDecimal val = new BigDecimal(value);
            BigDecimal minVal = StringUtils.isNotBlank(min) ? new BigDecimal(min) : null;
            BigDecimal maxVal = StringUtils.isNotBlank(max) ? new BigDecimal(max) : null;

            // 根据操作符判断
            if (AlarmRule.OP_LT.equals(operator) && maxVal != null) {
                return val.compareTo(maxVal) < 0;
            }
            if (AlarmRule.OP_LE.equals(operator) && maxVal != null) {
                return val.compareTo(maxVal) <= 0;
            }
            if (AlarmRule.OP_GT.equals(operator) && minVal != null) {
                return val.compareTo(minVal) > 0;
            }
            if (AlarmRule.OP_GE.equals(operator) && minVal != null) {
                return val.compareTo(minVal) >= 0;
            }

            // 默认范围检查：超出范围则触发
            boolean outOfRange = false;
            if (minVal != null && val.compareTo(minVal) < 0) {
                outOfRange = true;
            }
            if (maxVal != null && val.compareTo(maxVal) > 0) {
                outOfRange = true;
            }
            return outOfRange;

        } catch (NumberFormatException e) {
            log.warn("数值格式错误: value={}", value);
            return false;
        }
    }

    /**
     * 等值检查
     */
    private boolean checkEqual(String value, String expected, String operator) {
        if (AlarmRule.OP_NE.equals(operator)) {
            return !StringUtils.equals(value, expected);
        }
        // 默认等值检查
        return StringUtils.equals(value, expected);
    }

    /**
     * 非空检查
     */
    private boolean checkNotNull(String value, String operator) {
        boolean isNull = StringUtils.isBlank(value);
        if (AlarmRule.OP_NE.equals(operator)) {
            // 期望非空，值为空则触发
            return isNull;
        }
        // 默认：值为空则触发
        return isNull;
    }

    /**
     * 在线状态检查
     */
    private boolean checkOnline(String value, String expected) {
        String onlineValue = StringUtils.isNotBlank(expected) ? expected : "1";
        // 不等于在线值则触发告警
        return !StringUtils.equals(value, onlineValue);
    }

    /**
     * 检查是否恢复
     */
    private boolean checkRecovery(AlarmRule rule, String value) {
        if (StringUtils.isBlank(value)) {
            return false;
        }

        // 如果有单独的恢复阈值
        String recoveryThreshold = rule.getRecoveryThreshold();
        if (StringUtils.isNotBlank(recoveryThreshold)) {
            return StringUtils.equals(value, recoveryThreshold);
        }

        // 默认：检查是否回到正常范围
        switch (rule.getCheckType()) {
            case AlarmRule.CHECK_TYPE_RANGE:
                return !checkRange(value, rule.getThresholdMin(), rule.getThresholdMax(), rule.getOperator());
            case AlarmRule.CHECK_TYPE_EQUAL:
                return !checkEqual(value, rule.getThresholdValue(), rule.getOperator());
            case AlarmRule.CHECK_TYPE_ONLINE:
                return !checkOnline(value, rule.getThresholdValue());
            default:
                return false;
        }
    }

    /**
     * 渲染告警消息
     */
    private String renderMessage(AlarmRule rule, EmsDevice device, String attrValue) {
        String template = rule.getAlarmMsgTemplate();
        if (StringUtils.isBlank(template)) {
            // 默认模板
            template = "设备【${targetName}】${attrName}异常，当前值：${attrValue}，阈值：${threshold}";
        }

        // 构建变量Map
        Map<String, String> vars = new HashMap<>();
        vars.put("targetCode", device.getDeviceCode());
        vars.put("targetName", device.getDeviceName() != null ? device.getDeviceName() : device.getDeviceCode());
        vars.put("deviceModel", device.getDeviceModel() != null ? device.getDeviceModel() : "");
        vars.put("location", device.getLocation() != null ? device.getLocation() : "");
        vars.put("areaCode", device.getAreaCode() != null ? device.getAreaCode() : "");
        vars.put("attrKey", rule.getAttrKey() != null ? rule.getAttrKey() : "");
        vars.put("attrName", rule.getAttrName() != null ? rule.getAttrName() : rule.getAttrKey());
        vars.put("attrValue", attrValue != null ? attrValue : "");
        vars.put("threshold", rule.getThresholdDisplay());
        vars.put("ruleName", rule.getRuleName() != null ? rule.getRuleName() : "");
        vars.put("ruleCode", rule.getRuleCode() != null ? rule.getRuleCode() : "");

        // 替换变量
        Matcher matcher = TEMPLATE_VAR_PATTERN.matcher(template);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String varName = matcher.group(1);
            String varValue = vars.getOrDefault(varName, "");
            matcher.appendReplacement(sb, Matcher.quoteReplacement(varValue));
        }
        matcher.appendTail(sb);

        return sb.toString();
    }

    /**
     * 生成告警ID
     */
    private String generateAlarmId() {
        return "ALM" + System.currentTimeMillis() + String.format("%04d", new Random().nextInt(10000));
    }
}
