package com.supervision.util.mybatis;

import cn.hutool.crypto.digest.MD5;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

import java.util.ArrayList;
import java.util.Map;

/**
 * 一个基于mybatis简单粗暴的构建MappedStatement的实现
 */
public class RowMapperStatementBuilder {
    private final Configuration configuration;
    private final LanguageDriver languageDriver;

    public RowMapperStatementBuilder(Configuration configuration) {
        this.configuration = configuration;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
    }

    /**
     * 创建MSID
     *
     * @param sql 执行的sql
     * @param sql 执行的sqlCommandType
     * @return
     */
    private String generateMappedStatementId(String sql, SqlCommandType sqlCommandType) {
        return sqlCommandType.toString() + "." + MD5.create().digestHex(sql);
    }

    /**
     * 是否已经存在该ID
     *
     * @param msId
     * @return
     */
    private boolean hasMappedStatement(String msId) {
        return configuration.hasStatement(msId, false);
    }

    /**
     * 创建一个查询的MS
     *
     * @param msId
     * @param sqlSource  执行的sqlSource
     * @param resultType 返回的结果类型
     */
    private void cacheSelectMappedStatement(String msId, SqlSource sqlSource, final Class<?> resultType) {

        if (configuration.hasStatement(msId,false)){
            return;
        }
        MappedStatement ms = new MappedStatement.Builder(configuration, msId, sqlSource, SqlCommandType.SELECT)
                .resultMaps(new ArrayList<ResultMap>() {
                    {
                        add(new ResultMap.Builder(configuration, "defaultResultMap", resultType, new ArrayList<ResultMapping>(0)).build());
                    }
                })
                .build();
        //缓存MappedStatement
        configuration.addMappedStatement(ms);
    }

    /**
     * 创建一个简单的MS
     *
     * @param msId
     * @param sqlSource      执行的sqlSource
     * @param sqlCommandType 执行的sqlCommandType
     */
    private void newUpdateMappedStatement(String msId, SqlSource sqlSource, SqlCommandType sqlCommandType) {
        MappedStatement ms = new MappedStatement.Builder(configuration, msId, sqlSource, sqlCommandType)
                .resultMaps(new ArrayList<ResultMap>() {
                    {
                        add(new ResultMap.Builder(configuration, "defaultResultMap", int.class, new ArrayList<ResultMapping>(0)).build());
                    }
                })
                .build();
        //缓存
        configuration.addMappedStatement(ms);
    }

    public String select(String sql) {
        String msId = generateMappedStatementId(sql, SqlCommandType.SELECT);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
        cacheSelectMappedStatement(msId, sqlSource, Map.class);
        return msId;
    }

    public String selectDynamic(String sql, Class<?> parameterType) {
        String msId = generateMappedStatementId(sql , SqlCommandType.SELECT);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
        cacheSelectMappedStatement(msId, sqlSource, Map.class);
        return msId;
    }

    public String select(String sql, Class<?> resultType) {
        String msId = generateMappedStatementId(resultType + sql, SqlCommandType.SELECT);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
        cacheSelectMappedStatement(msId, sqlSource, resultType);
        return msId;
    }

    public String selectDynamic(String sql, Class<?> parameterType, Class<?> resultType) {
        String msId = generateMappedStatementId(resultType + sql + parameterType, SqlCommandType.SELECT);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
        cacheSelectMappedStatement(msId, sqlSource, resultType);
        return msId;
    }

    public String insert(String sql) {
        String msId = generateMappedStatementId(sql, SqlCommandType.INSERT);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
        newUpdateMappedStatement(msId, sqlSource, SqlCommandType.INSERT);
        return msId;
    }

    public String insertDynamic(String sql, Class<?> parameterType) {
        String msId = generateMappedStatementId(sql + parameterType, SqlCommandType.INSERT);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
        newUpdateMappedStatement(msId, sqlSource, SqlCommandType.INSERT);
        return msId;
    }

    public String update(String sql) {
        String msId = generateMappedStatementId(sql, SqlCommandType.UPDATE);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
        newUpdateMappedStatement(msId, sqlSource, SqlCommandType.UPDATE);
        return msId;
    }

    public String updateDynamic(String sql, Class<?> parameterType) {
        String msId = generateMappedStatementId(sql + parameterType, SqlCommandType.UPDATE);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
        newUpdateMappedStatement(msId, sqlSource, SqlCommandType.UPDATE);
        return msId;
    }

    public String delete(String sql) {
        String msId = generateMappedStatementId(sql, SqlCommandType.DELETE);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        StaticSqlSource sqlSource = new StaticSqlSource(configuration, sql);
        newUpdateMappedStatement(msId, sqlSource, SqlCommandType.DELETE);
        return msId;
    }

    public String deleteDynamic(String sql, Class<?> parameterType) {
        String msId = generateMappedStatementId(sql + parameterType, SqlCommandType.DELETE);
        if (hasMappedStatement(msId)) {
            return msId;
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, parameterType);
        newUpdateMappedStatement(msId, sqlSource, SqlCommandType.DELETE);
        return msId;
    }
}
