package com.yfd.platform.utils;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

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

/******************************
 * 用途说明: 特殊字符处理
 * 作者姓名: tw
 * 创建时间: 2023/11/09 10:50
 ******************************/
public class MybatisUtil {


    /**
     * 检查sql中，是否含有like查询
     */
    public static final Pattern LIKE_PARAM_PATTERN =
            Pattern.compile("like\\s(concat)?[\\w'\"\\(\\)\\%,\\s\\n\\t]*\\?", Pattern.CASE_INSENSITIVE);

    public static void escapeParameterIfContainingLike(MappedStatement ms, BoundSql boundSql) {
        final SqlCommandType sqlCommandType = ms.getSqlCommandType();
        final StatementType statementType = ms.getStatementType();
        // 只处理 有参数的查询语句
        if (SqlCommandType.SELECT == sqlCommandType && StatementType.PREPARED == statementType) {
            escapeParameterIfContainingLike(boundSql);
        }
    }

    public static void escapeParameterIfContainingLike(BoundSql boundSql) {
        if (null == boundSql) {
            return;
        }
        final String prepareSql = boundSql.getSql();
        final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

        // 找到 like 后面的参数
        final List<Integer> positions = findLikeParam(prepareSql);
        if (positions.isEmpty()) {
            return;
        }

        final List<ParameterMapping> likeParameterMappings = new ArrayList<>(parameterMappings.size());
        // 复制
        final MetaObject metaObject = SystemMetaObject.forObject(boundSql.getParameterObject());
        for (ParameterMapping parameterMapping : parameterMappings) {
            final String property = parameterMapping.getProperty();
            if (!metaObject.hasGetter(property)) {
                continue;
            }
            boundSql.setAdditionalParameter(property, metaObject.getValue(property));
        }

        for (Integer position : positions) {
            final ParameterMapping parameterMapping = parameterMappings.get(position);
            likeParameterMappings.add(parameterMapping);
        }
        // 覆盖 转译字符
        delegateMetaParameterForEscape(boundSql, likeParameterMappings);
    }

    /**
     * @param boundSql              原BoundSql
     * @param likeParameterMappings 需要转义的参数
     */
    private static void delegateMetaParameterForEscape(BoundSql boundSql,
                                                       List<ParameterMapping> likeParameterMappings) {
        final MetaObject metaObject = SystemMetaObject.forObject(boundSql.getParameterObject());
        for (ParameterMapping mapping : likeParameterMappings) {
            final String property = mapping.getProperty();
            if (!metaObject.hasGetter(property)) {
                continue;
            }
            final Object value = metaObject.getValue(property);
            boolean flag = false;
            if (value instanceof String) {
                String newValue = (String) value;
                if (property.contains("paramNameValuePairs")) {
                    newValue = StrUtil.replaceLast(StrUtil.replaceFirst(newValue, "%", ""), "%", "");
                    flag = true;
                }
                final String saveValue = convertToSqlSafeValue(newValue, flag);
                boundSql.setAdditionalParameter(property, saveValue);
            }
        }
    }

    /**
     * 匹配like 位置， 如
     * like concat('%',?,'%'）
     * like CONCAT('%',?,'%'）
     * LIKE CONCAT('%', ?,'%'）
     * lIKE conCAT('%', ?,'%'）
     * like ?
     */
    private static List<Integer> findLikeParam(String prepareSql) {
        if (CharSequenceUtil.isBlank(prepareSql)) {
            return Collections.emptyList();
        }
        final Matcher matcher = LIKE_PARAM_PATTERN.matcher(prepareSql);
        if (!matcher.find()) {
            return Collections.emptyList();
        }

        matcher.reset();
        int pos = 0;
        final List<Integer> indexes = new ArrayList<>();
        while (matcher.find(pos)) {
            final int start = matcher.start();
            final int index = CharSequenceUtil.count(prepareSql.substring(0, start), '?');
            indexes.add(index);
            pos = matcher.end();
        }
        return indexes;
    }

    /**
     * 转译 \ % _
     * 禁止与escape 同时使用
     */
    public static String convertToSqlSafeValue(String str, boolean flag) {
        if (CharSequenceUtil.isEmpty(str)) {
            return str;
        }
        String reg = str.replace("\\", "\\\\")
                .replace("%", "\\%")
                .replace("_", "\\_");
        if (flag) {
            String replace = reg;
            reg = "%" + replace + "%";
        }
        return reg;
    }


}
