package com.ruoyi.web.report.service.support;

import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * 报表 SQL 安全校验器。
 *
 * <p>提供运行态最基础的 SQL 安全约束：
 * 仅允许 SELECT、禁止危险关键字、限制标识符与表达式字符集。</p>
 */
@Component
public class ReportSqlSecurityValidator {

    /** 仅允许字母、数字、下划线、点号组成标识符（表名/字段名）。 */
    private static final Pattern SAFE_IDENTIFIER = Pattern.compile("^[A-Za-z_][A-Za-z0-9_\\.]*$");
    /** 允许的表达式字符范围（低代码表达式安全兜底）。 */
    private static final Pattern SAFE_EXPR = Pattern.compile("^[A-Za-z0-9_\\.\\s\\(\\),+\\-*/]*$");
    /** 禁止关键字（小写比较）。 */
    private static final Set<String> BLOCKED_KEYWORDS = new HashSet<>(Arrays.asList(
            " insert ", " update ", " delete ", " drop ", " truncate ",
            " alter ", " create ", " grant ", " revoke ", " execute "
    ));

    /**
     * 校验自定义 SQL 仅为单条 SELECT。
     *
     * @param sql SQL 文本
     */
    public void validateCustomSelectSql(String sql) {
        if (sql == null || sql.trim().isEmpty()) {
            throw new IllegalArgumentException("自定义SQL为空，无法执行查询。");
        }
        String normalized = " " + sql.trim().toLowerCase(Locale.ROOT).replaceAll("\\s+", " ") + " ";
        if (!normalized.trim().startsWith("select ")) {
            throw new IllegalArgumentException("仅允许执行 SELECT 查询。");
        }
        if (normalized.contains(";")) {
            throw new IllegalArgumentException("不允许多语句执行。");
        }
        for (String keyword : BLOCKED_KEYWORDS) {
            if (normalized.contains(keyword)) {
                throw new IllegalArgumentException("检测到高风险关键字: " + keyword.trim());
            }
        }
    }

    /**
     * 校验标识符安全性（表名、字段名、别名）。
     *
     * @param identifier 标识符
     */
    public void validateIdentifier(String identifier) {
        if (identifier == null || !SAFE_IDENTIFIER.matcher(identifier).matches()) {
            throw new IllegalArgumentException("非法标识符: " + identifier);
        }
    }

    /**
     * 校验低代码表达式字符集安全性。
     *
     * @param expr 表达式
     */
    public void validateExpression(String expr) {
        if (expr == null || expr.trim().isEmpty()) {
            return;
        }
        if (!SAFE_EXPR.matcher(expr).matches()) {
            throw new IllegalArgumentException("非法表达式: " + expr);
        }
    }
}
