package org.hzhub.erp.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * SQL安全验证工具
 *
 * @author HZHub Team
 */
public class SqlValidator {

    private static final Logger log = LoggerFactory.getLogger(SqlValidator.class);

    /**
     * SQL白名单关键字（允许的SQL关键字）
     */
    private static final Set<String> ALLOWED_KEYWORDS = new HashSet<>(Arrays.asList(
        "SELECT", "FROM", "WHERE", "AND", "OR", "ORDER", "BY", "GROUP", "HAVING",
        "OFFSET", "FETCH", "NEXT", "ROWS", "ONLY", "AS", "COUNT", "SUM", "AVG",
        "MIN", "MAX", "DISTINCT", "TOP", "INNER", "LEFT", "RIGHT", "JOIN", "ON",
        "IS", "NULL", "NOT", "IN", "LIKE", "BETWEEN", "EXISTS", "CASE", "WHEN",
        "THEN", "ELSE", "END", "UNION", "ALL", "WITH", "OVER", "PARTITION"
    ));

    /**
     * SQL危险关键字（禁止的SQL关键字）
     */
    private static final Set<String> DANGEROUS_KEYWORDS = new HashSet<>(Arrays.asList(
        "DROP", "DELETE", "TRUNCATE", "ALTER", "CREATE", "GRANT", "REVOKE",
        "INSERT", "UPDATE", "EXEC", "EXECUTE", "MERGE", "CALL"
    ));

    /**
     * 验证SQL安全性
     *
     * @param sql SQL语句
     * @return 是否安全
     */
    public static boolean validate(String sql) {
        if (sql == null || sql.trim().isEmpty()) {
            return false;
        }

        // 提取SQL关键字
        String[] words = sql.toUpperCase().split("\\s+");

        // 检查危险关键字
        for (String word : words) {
            if (DANGEROUS_KEYWORDS.contains(word)) {
                log.error("SQL包含危险关键字: {}", word);
                return false;
            }
        }

        // 检查是否以SELECT开头（只允许查询语句）
        String upperSql = sql.trim().toUpperCase();
        if (!upperSql.startsWith("SELECT")) {
            log.error("SQL不是查询语句，必须以SELECT开头");
            return false;
        }

        // 检查分号（防止多条SQL注入）
        if (sql.contains(";") && !sql.trim().endsWith(";")) {
            log.error("SQL包含多个语句（分号），可能存在注入风险");
            return false;
        }

        return true;
    }

    /**
     * 检查是否包含参数占位符
     *
     * @param sql SQL语句
     * @return 是否包含参数占位符 #{paramName}
     */
    public static boolean containsParams(String sql) {
        return sql != null && sql.contains("#{") && sql.contains("}");
    }

    /**
     * 提取参数名称列表
     *
     * @param sql SQL语句
     * @return 参数名称列表
     */
    public static Set<String> extractParamNames(String sql) {
        Set<String> paramNames = new HashSet<>();

        if (sql == null) {
            return paramNames;
        }

        // 提取 #{paramName} 中的参数名
        int start = 0;
        while (start < sql.length()) {
            int begin = sql.indexOf("#{", start);
            if (begin == -1) {
                break;
            }
            int end = sql.indexOf("}", begin);
            if (end == -1) {
                break;
            }

            String paramName = sql.substring(begin + 2, end).trim();
            paramNames.add(paramName);
            start = end + 1;
        }

        return paramNames;
    }
}