package com.changhu.support.mybatisplus.interceptor;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.locationtech.jts.geom.*;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.changhu.common.utils.GeometryUtil.ST_GeomFromText;


/**
 * @author 20252
 * @createTime 2024/6/6 下午4:32
 * @desc 注意!!!此拦截器只针对mp提供的基本构造方法管用 可以进行参数对象拦截 如果是xml，请自行添加ST_GeomFromText函数
 */
@Slf4j
public class GeometryInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {


    public static final List<Class<? extends Geometry>> GEOMETRY_CLASS_LIST = CollUtil.newArrayList(
            Point.class,
            MultiPoint.class,
            Polygon.class,
            MultiPolygon.class,
            LineString.class,
            MultiLineString.class
    );

    public GeometryInnerInterceptor() {
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
        MappedStatement ms = mpSh.mappedStatement();
        //sql类型：INSERT UPDATE DELETE
        SqlCommandType sct = ms.getSqlCommandType();
        //mp参数
        Object parameter = mpSh.parameterHandler().getParameterObject();
        //参数对象
        Object object = null;
        //获取参数的属性
        switch (sct) {
            case UPDATE:
                try {
                    object = BeanUtil.beanToMap(parameter).get(Constants.ENTITY);
                } catch (Exception ignored) {
                }
                break;
            case INSERT:
                object = parameter;
                break;
        }
        //如果没有参数 就不进行sql修改
        if (object == null) {
            return;
        }
        //获取 parameter 对象 中有关 geometry 的属性名对应
        List<String> geoFields = this.getGeoFields(object);
        //如果没有geometry字段 就不进行sql修改
        if (geoFields.isEmpty()) {
            return;
        }
        //获取原始sql
        BoundSql boundSql = ms.getBoundSql(parameter);
        //UPDATE geo_test SET update_by=?,update_time=?,delete_flag=1 WHERE id=? AND delete_flag=0
        String originalSql = StrUtil.removeAllLineBreaks(boundSql.getSql());
        switch (sct) {
            case UPDATE:
                for (String geometryField : geoFields) {
                    String regex = geometryField + "\\s*=\\s*\\?";
                    originalSql = ReUtil.replaceAll(originalSql, regex, StrUtil.format("{}={}", geometryField, ST_GeomFromText));
                }
                break;
            case INSERT:
                for (String geometryField : geoFields) {
                    originalSql = this.replaceFindGeo(originalSql, geometryField);
                }
                break;
        }
        PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
        //修改sql
        mpBs.sql(originalSql);
    }

    private String replaceFindGeo(String originalSql, String geometryField) {
        List<String> split = StrUtil.split(originalSql, geometryField);
        //找出geo字段在sql中的位置
        int geometryFieldIndex = StrUtil.count(split.get(0), ",");
        StringBuilder stringBuffer = new StringBuilder();
        Matcher matcher = Pattern.compile("\\?").matcher(originalSql);
        int count = 0;
        while (matcher.find()) {
            if (count == geometryFieldIndex) {
                matcher.appendReplacement(stringBuffer, ST_GeomFromText);
                break;
            }
            count++;
        }
        matcher.appendTail(stringBuffer);
        return stringBuffer.toString();
    }

    /**
     * 获取对象中的geo字段
     */
    private List<String> getGeoFields(Object object) {
        return Arrays.stream(ReflectUtil.getFields(object.getClass()))
                .filter(field -> GEOMETRY_CLASS_LIST.contains(field.getType()))
                .map(Field::getName)
                .collect(Collectors.toList());
    }

}
