package com.foreverwin.mesnac.console.mybatis.core;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.TableInfoHelper;
import com.foreverwin.mesnac.console.mybatis.override.MethodFactory;
import com.foreverwin.modular.core.exception.BusinessException;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;

/**
 * @program: modular-aps
 * @description: override mybatis configuration
 * @author: syngna
 * @create: 2021-04-16 17:15
 */
public class ExtMybatisConfiguration extends MybatisConfiguration {

    protected final ExtMybatisMapperRegistry mybatisMapperRegistry = new ExtMybatisMapperRegistry(this);

    /**
     * 使用自己的 MybatisMapperRegistry
     */
    @Override
    public MapperRegistry getMapperRegistry() {
        return mybatisMapperRegistry;
    }

    /**
     * 使用自己的 MybatisMapperRegistry
     */
    @Override
    public <T> void addMapper(Class<T> type) {
        mybatisMapperRegistry.addMapper(type);
    }

    /**
     * 使用自己的 MybatisMapperRegistry
     */
    @Override
    public void addMappers(String packageName, Class<?> superType) {
        mybatisMapperRegistry.addMappers(packageName, superType);
    }

    /**
     * 使用自己的 MybatisMapperRegistry
     */
    @Override
    public void addMappers(String packageName) {
        mybatisMapperRegistry.addMappers(packageName);
    }

    /**
     * 使用自己的 MybatisMapperRegistry
     */
    @Override
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mybatisMapperRegistry.getMapper(type, sqlSession);
    }

    /**
     * 使用自己的 MybatisMapperRegistry
     */
    @Override
    public boolean hasMapper(Class<?> type) {
        return mybatisMapperRegistry.hasMapper(type);
    }

    @Override
    public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
        if (validateIncompleteStatements) {
            buildAllStatements();
        }
        if(!this.hasStatement(id)) {
            // 新增逻辑，没找到注入的statement则先注入----------------------------------------------------------------------------------------------------------
            String mapperName = id.substring(0, id.lastIndexOf("."));
            String methodName = id.substring(id.lastIndexOf(".") + 1);
            Class<?> mapperInterface;
            try {
                mapperInterface = Class.forName(mapperName);
            } catch (ClassNotFoundException e) {
                throw BusinessException.build("get mapper class [" + mapperName + "] failed " + e);
            }
            String resource = mapperName.replace('.', '/') + ".java (best guess)";
            MapperBuilderAssistant assistant = new MapperBuilderAssistant(this, resource);
            assistant.setCurrentNamespace(mapperName);
            Class<?> modelClass = extractModelClass(mapperInterface);
            TableInfo tableInfo = TableInfoHelper.initTableInfo(assistant, modelClass);
            MethodFactory.getInstance(methodName).inject(assistant,mapperInterface, modelClass, tableInfo);
            // -----------------------------------------------------------------------------------------------------------------------------------------------
        }
        return mappedStatements.get(id);
    }

    /**
     * 提取泛型模型,多泛型的时候请将泛型T放在第一位
     *
     * @param mapperClass mapper 接口
     * @return mapper 泛型
     */
    protected Class<?> extractModelClass(Class<?> mapperClass) {
        Type[] types = mapperClass.getGenericInterfaces();
        ParameterizedType target = null;
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                Type[] typeArray = ((ParameterizedType) type).getActualTypeArguments();
                if (ArrayUtils.isNotEmpty(typeArray)) {
                    for (Type t : typeArray) {
                        if (t instanceof TypeVariable || t instanceof WildcardType) {
                            break;
                        } else {
                            target = (ParameterizedType) type;
                            break;
                        }
                    }
                }
                break;
            }
        }
        return target == null ? null : (Class<?>) target.getActualTypeArguments()[0];
    }
}