package com.cf.imes.module.report.validation.datasource;

import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.framework.common.constants.ResourceBundleConstants;
import com.cf.imes.framework.common.util.i18n.core.util.ResourceBundleUtils;
import org.apache.commons.lang3.StringUtils;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.context.i18n.LocaleContextHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 非法sql校验器
 *
 * @author Gqr
 * @since 2024/10/21 18:44
 */
public class SqlIllegalValidator implements ConstraintValidator<SqlIllegalValid, String> {

    private static final Pattern annotationPattern = Pattern.compile("/\\*[\\s\\S]*\\*/");

    private static final String[] someKeywordPattern = new String[]{"chr\\s*\\(", "mid\\s*\\(", " char\\s*\\(", "sleep\\s*\\(", "user\\s*\\(", "show\\s+tables", "user[\\s]*\\([\\s]*\\)", "show\\s+databases", "sleep\\(\\d*\\)", "sleep\\(.*\\)"};

    private static final List<String> specialSymbolList = new ArrayList<>();

    private static final String REPORT_DATASOURCE_SQL_NOTE_INVALID_I18N_KEY = "report.datasource.sql.note.invalid";
    private static final String REPORT_DATASOURCE_SQL_INJECT_RISKS_I18N_KEY = "report.datasource.sql.inject.risks";

    static {
        specialSymbolList.add(";");
        specialSymbolList.add("+");
        specialSymbolList.add("--");
    }

    @Override
    public boolean isValid(String sql, ConstraintValidatorContext context) {
        if (StringUtils.isEmpty(sql)) {
            return true;
        }
        // 校验注释
        if (hasAnnotation(sql)) {
            context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
            context.buildConstraintViolationWithTemplate(ResourceBundleUtils.getMessage(ResourceBundleConstants.MESSAGE_ENUM_PATH, REPORT_DATASOURCE_SQL_NOTE_INVALID_I18N_KEY, REPORT_DATASOURCE_SQL_NOTE_INVALID_I18N_KEY, LocaleContextHolder.getLocale())).addConstraintViolation();
            return false;
        }
        sql = sql.toLowerCase();
        sql = sql.replaceAll("/\\*.*\\*/", "");
        // 敏感关键字数组
        String[] arrayOfSensitiveKey = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |alter |delete |grant |update |drop |master |truncate |declare |--|".split("\\|");
        // 校验敏感关键字
        for (byte b = 0; b < arrayOfSensitiveKey.length; b++) {
            if (checkSensitiveKey(sql, arrayOfSensitiveKey[b])) {
                context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
                context.buildConstraintViolationWithTemplate(ResourceBundleUtils.getMessage(ResourceBundleConstants.MESSAGE_ENUM_PATH, REPORT_DATASOURCE_SQL_INJECT_RISKS_I18N_KEY, REPORT_DATASOURCE_SQL_INJECT_RISKS_I18N_KEY, LocaleContextHolder.getLocale())).addConstraintViolation();
                return false;
            }
        }
        // 校验一些函数关键字
        for (String skw : someKeywordPattern) {
            String someKeywordStr = ".*" + skw + ".*";
            if (Pattern.matches(someKeywordStr, sql)) {
                context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
                context.buildConstraintViolationWithTemplate(ResourceBundleUtils.getMessage(ResourceBundleConstants.MESSAGE_ENUM_PATH, REPORT_DATASOURCE_SQL_INJECT_RISKS_I18N_KEY, REPORT_DATASOURCE_SQL_INJECT_RISKS_I18N_KEY, LocaleContextHolder.getLocale())).addConstraintViolation();
                return false;
            }
        }

        return true;
    }

    /**
     * 检查敏感关键字
     *
     * @param sql
     * @param sensitiveKey
     * @return true：带敏感关键字，反之不带
     */
    private boolean checkSensitiveKey(String sql, String sensitiveKey) {
        if (sql.startsWith(sensitiveKey.trim())) {
            return true;
        }
        if (sql.contains(sensitiveKey)) {
            String sk1 = " " + sensitiveKey;
            if (specialSymbolList.contains(sensitiveKey)) {
                sk1 = sensitiveKey;
            }
            if (sql.contains(sk1)) {
                return true;
            }
            String sk2 = "\\s+\\S+" + sensitiveKey;
            List<String> list = matchInjectKeyword(sk2, sql);
            for (String keyword : list) {
                if (keyword.contains("%") || keyword.contains("+") || keyword.contains("#") || keyword.contains("/") || keyword.contains(")")) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 校验注释
     *
     * @param sql
     * @return
     */
    private boolean hasAnnotation(String sql) {
        // 不允许存在注释
        if (sql.contains("--")) {
            return true;
        } else {
            Matcher var1 = annotationPattern.matcher(sql);
            if (var1.find()) {
                return true;
            }
        }
        return false;
    }


    /**
     * 匹配注入关键字
     *
     * @param sensitiveKey
     * @param sql
     * @return
     */
    public List<String> matchInjectKeyword(String sensitiveKey, CharSequence sql) {
        Pattern paramPattern = Pattern.compile(sensitiveKey, 32);
        List<String> paramT = new ArrayList<>();
        if (ObjectUtil.isAllNotEmpty(paramPattern, sql)) {
            Matcher matcher = paramPattern.matcher(sql);
            while (matcher.find()) {
                paramT.add(matcher.group(0));
            }
        }
        return paramT;
    }
}
