package tech.easyflow.ai.node;

import com.easyagents.flow.core.util.StringUtil;
import com.google.re2j.Pattern;
import com.google.re2j.PatternSyntaxException;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * 条件判断节点规则校验与安全正则执行支持。
 */
public final class ConditionRuleSupport {

    public static final String OPERATOR_REGEX_MATCH = "regexMatch";
    public static final int MAX_REGEX_LENGTH = 512;

    private static final int MAX_REGEX_CACHE_SIZE = 256;
    private static final Set<String> SUPPORTED_OPERATORS = Set.of(
            "eq",
            "ne",
            "gt",
            "gte",
            "lt",
            "lte",
            "isEmpty",
            "isNotEmpty",
            "contains",
            "notContains",
            OPERATOR_REGEX_MATCH);
    private static final Map<String, Pattern> REGEX_CACHE =
            new LinkedHashMap<String, Pattern>(32, 0.75F, true) {
                private static final long serialVersionUID = 1L;

                @Override
                protected boolean removeEldestEntry(Map.Entry<String, Pattern> eldest) {
                    return size() > MAX_REGEX_CACHE_SIZE;
                }
            };

    /**
     * 禁止创建工具类实例。
     */
    private ConditionRuleSupport() {
    }

    /**
     * 校验一条可视化条件规则。
     *
     * @param rule 条件规则
     * @return 校验错误；返回 {@code null} 表示通过
     */
    public static String validateRule(ConditionNode.ConditionRule rule) {
        if (rule == null) {
            return "条件规则不能为空";
        }

        String operator = rule.getOperator();
        if (StringUtil.noText(operator)) {
            return "条件操作符不能为空";
        }
        if (!SUPPORTED_OPERATORS.contains(operator)) {
            return "不支持的条件操作符: " + operator;
        }
        if (!OPERATOR_REGEX_MATCH.equals(operator)) {
            return null;
        }
        if (!"fixed".equals(rule.getRightType())) {
            return "正则表达式必须使用固定值";
        }
        return validateRegex(rule.getRightValue());
    }

    /**
     * 校验正则表达式的长度和 RE2/J 语法。
     *
     * @param regex 正则表达式
     * @return 校验错误；返回 {@code null} 表示通过
     */
    public static String validateRegex(String regex) {
        try {
            requireRegex(regex);
            return null;
        } catch (IllegalArgumentException e) {
            return e.getMessage();
        }
    }

    /**
     * 使用 RE2/J 在标量左值中查找正则匹配。
     *
     * @param leftValue 条件左值
     * @param regex 正则表达式
     * @return 任意位置存在匹配时返回 {@code true}
     * @throws IllegalArgumentException 正则无效或左值类型不支持
     */
    public static boolean matchesRegex(Object leftValue, String regex) {
        Pattern pattern = requireRegex(regex);
        if (leftValue == null) {
            return false;
        }
        if (!(leftValue instanceof CharSequence)
                && !(leftValue instanceof Number)
                && !(leftValue instanceof Boolean)
                && !(leftValue instanceof Character)) {
            throw new IllegalArgumentException(
                    "正则匹配仅支持字符串、数字或布尔值，当前类型: "
                            + leftValue.getClass().getSimpleName());
        }
        return pattern.matcher(String.valueOf(leftValue)).find();
    }

    /**
     * 校验并获取正则模式。
     *
     * @param regex 正则表达式
     * @return 已编译 RE2/J 模式
     * @throws IllegalArgumentException 正则为空、过长或语法无效
     */
    private static Pattern requireRegex(String regex) {
        if (StringUtil.noText(regex)) {
            throw new IllegalArgumentException("正则表达式不能为空");
        }
        if (regex.length() > MAX_REGEX_LENGTH) {
            throw new IllegalArgumentException(
                    "正则表达式不能超过 " + MAX_REGEX_LENGTH + " 个字符");
        }
        try {
            return compileRegex(regex);
        } catch (PatternSyntaxException e) {
            throw new IllegalArgumentException(
                    "正则表达式语法错误: " + e.getMessage(), e);
        }
    }

    /**
     * 获取已编译正则，并以有界 LRU 缓存复用结果。
     *
     * @param regex 正则表达式
     * @return 已编译 RE2/J 模式
     * @throws PatternSyntaxException 正则语法无效
     */
    private static Pattern compileRegex(String regex) {
        synchronized (REGEX_CACHE) {
            Pattern cached = REGEX_CACHE.get(regex);
            if (cached != null) {
                return cached;
            }
            Pattern compiled = Pattern.compile(regex);
            REGEX_CACHE.put(regex, compiled);
            return compiled;
        }
    }
}
