package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;

import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import lombok.extern.slf4j.Slf4j;

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

import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;

/**
 * IoT 场景规则匹配器工具类：提供通用的条件评估逻辑和工具方法，供触发器和条件匹配器使用
 *
 * 该类包含了匹配器实现中常用的工具方法，如条件评估、参数校验、日志记录等
 *
 * @author HUIHUI
 */
@Slf4j
public final class IotSceneRuleMatcherHelper {

    /**
     * 私有构造函数，防止实例化
     */
    private IotSceneRuleMatcherHelper() {
    }

    /**
     * 评估条件是否匹配
     *
     * @param sourceValue 源值（来自消息）
     * @param operator    操作符
     * @param paramValue  参数值（来自条件配置）
     * @return 是否匹配
     */
    public static boolean evaluateCondition(Object sourceValue, String operator, String paramValue) {
        try {
            // 1. 校验操作符是否合法
            IotSceneRuleConditionOperatorEnum operatorEnum = IotSceneRuleConditionOperatorEnum.operatorOf(operator);
            if (operatorEnum == null) {
                log.warn("[evaluateCondition][operator({}) 操作符无效]", operator);
                return false;
            }

            // 2. 构建 Spring 表达式变量
            return evaluateConditionWithOperatorEnum(sourceValue, operatorEnum, paramValue);
        } catch (Exception e) {
            log.error("[evaluateCondition][sourceValue({}) operator({}) paramValue({}) 条件评估异常]",
                    sourceValue, operator, paramValue, e);
            return false;
        }
    }

    /**
     * 使用操作符枚举评估条件是否匹配
     *
     * @param sourceValue  源值（来自消息）
     * @param operatorEnum 操作符枚举
     * @param paramValue   参数值（来自条件配置）
     * @return 是否匹配
     */
    @SuppressWarnings("DataFlowIssue")
    public static boolean evaluateConditionWithOperatorEnum(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {
        try {
            // 1. 构建 Spring 表达式变量
            Map<String, Object> springExpressionVariables = buildSpringExpressionVariables(sourceValue, operatorEnum, paramValue);

            // 2. 计算 Spring 表达式
            return (Boolean) SpringExpressionUtils.parseExpression(operatorEnum.getSpringExpression(), springExpressionVariables);
        } catch (Exception e) {
            log.error("[evaluateConditionWithOperatorEnum][sourceValue({}) operatorEnum({}) paramValue({}) 条件评估异常]",
                    sourceValue, operatorEnum, paramValue, e);
            return false;
        }
    }

    /**
     * 构建 Spring 表达式变量
     */
    private static Map<String, Object> buildSpringExpressionVariables(Object sourceValue, IotSceneRuleConditionOperatorEnum operatorEnum, String paramValue) {
        Map<String, Object> springExpressionVariables = new HashMap<>();

        // 设置源值
        springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, StrUtil.toString(sourceValue));

        // 处理参数值
        if (StrUtil.isNotBlank(paramValue)) {
            List<String> parameterValues = StrUtil.splitTrim(paramValue, CharPool.COMMA);

            // 设置原始参数值
            springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, paramValue);
            springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);

            // 特殊处理：解决数字比较问题
            // Spring 表达式基于 compareTo 方法，对数字的比较存在问题，需要转换为数字类型
            if (isNumericComparisonOperator(operatorEnum) && isNumericComparison(sourceValue, parameterValues)) {
                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE,
                        NumberUtil.parseDouble(String.valueOf(sourceValue)));
                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
                        NumberUtil.parseDouble(paramValue));
                springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
                        convertList(parameterValues, NumberUtil::parseDouble));
            }
        }

        return springExpressionVariables;
    }

    /**
     * 判断是否为数字比较操作符
     */
    private static boolean isNumericComparisonOperator(IotSceneRuleConditionOperatorEnum operatorEnum) {
        return ObjectUtils.equalsAny(operatorEnum,
                IotSceneRuleConditionOperatorEnum.BETWEEN,
                IotSceneRuleConditionOperatorEnum.NOT_BETWEEN,
                IotSceneRuleConditionOperatorEnum.GREATER_THAN,
                IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS,
                IotSceneRuleConditionOperatorEnum.LESS_THAN,
                IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS);
    }

    /**
     * 判断是否为数字比较场景
     */
    private static boolean isNumericComparison(Object sourceValue, List<String> parameterValues) {
        return NumberUtil.isNumber(String.valueOf(sourceValue)) && NumberUtils.isAllNumber(parameterValues);
    }

    // ========== 【触发器】相关工具方法 ==========

    /**
     * 检查基础触发器参数是否有效
     *
     * @param trigger 触发器配置
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isBasicTriggerValid(IotSceneRuleDO.Trigger trigger) {
        return trigger != null && trigger.getType() != null;
    }

    /**
     * 检查触发器操作符和值是否有效
     *
     * @param trigger 触发器配置
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isTriggerOperatorAndValueValid(IotSceneRuleDO.Trigger trigger) {
        return StrUtil.isNotBlank(trigger.getOperator()) && StrUtil.isNotBlank(trigger.getValue());
    }

    /**
     * 记录触发器匹配成功日志
     *
     * @param message     设备消息
     * @param trigger     触发器配置
     */
    public static void logTriggerMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger) {
        log.debug("[isMatched][message({}) trigger({}) 匹配触发器成功]",
                message != null ? message.getRequestId() : null,
                trigger != null ? trigger.getType() : null);
    }

    /**
     * 记录触发器匹配失败日志
     *
     * @param message     设备消息
     * @param trigger     触发器配置
     * @param reason      失败原因
     */
    public static void logTriggerMatchFailure(IotDeviceMessage message, IotSceneRuleDO.Trigger trigger, String reason) {
        log.debug("[isMatched][message({}) trigger({}) reason({}) 匹配触发器失败]",
                message != null ? message.getRequestId() : null,
                trigger != null ? trigger.getType() : null,
                reason);
    }

    // ========== 【条件】相关工具方法 ==========

    /**
     * 检查基础条件参数是否有效
     *
     * @param condition 触发条件
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isBasicConditionValid(IotSceneRuleDO.TriggerCondition condition) {
        return condition != null && condition.getType() != null;
    }

    /**
     * 检查条件操作符和参数是否有效
     *
     * @param condition 触发条件
     * @return 是否有效
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isConditionOperatorAndParamValid(IotSceneRuleDO.TriggerCondition condition) {
        return StrUtil.isNotBlank(condition.getOperator()) && StrUtil.isNotBlank(condition.getParam());
    }

    /**
     * 记录条件匹配成功日志
     *
     * @param message     设备消息
     * @param condition   触发条件
     */
    public static void logConditionMatchSuccess(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition) {
        log.debug("[isMatched][message({}) condition({}) 匹配条件成功]",
                message != null ? message.getRequestId() : null,
                condition != null ? condition.getType() : null);
    }

    /**
     * 记录条件匹配失败日志
     *
     * @param message     设备消息
     * @param condition   触发条件
     * @param reason      失败原因
     */
    public static void logConditionMatchFailure(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, String reason) {
        log.debug("[isMatched][message({}) condition({}) reason({}) 匹配条件失败]",
                message != null ? message.getRequestId() : null,
                condition != null ? condition.getType() : null,
                reason);
    }

    // ========== 【通用】工具方法 ==========

    /**
     * 检查标识符是否匹配
     *
     * @param expectedIdentifier 期望的标识符
     * @param actualIdentifier   实际的标识符
     * @return 是否匹配
     */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    public static boolean isIdentifierMatched(String expectedIdentifier, String actualIdentifier) {
        return StrUtil.isNotBlank(expectedIdentifier) && expectedIdentifier.equals(actualIdentifier);
    }

}
