package cd.casic.plugin.core.register.mybatis;

import cd.casic.plugin.dataobject.pojo.PluginInfo;
import cd.casic.plugin.function.IPluginProcessor;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.ClassUtils;

import java.util.Set;

/**
 * @description: TODO
 * @author: mianbin
 * @date: 2025/5/22 19:59
 * @version: 1.0
 */
@Slf4j
public class MybatisPlusProcessor implements IPluginProcessor {
    @Override
    public void initialize() throws Exception {

    }

    @Override
    public void registry(PluginInfo plugin) throws Exception {
        MybatisPlusConfig config = getObjectByInterfaceClass(plugin.getPluginConfigObjects(), MybatisPlusConfig.class);
        if (config == null) return;
        final MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();

        if (config.enableOneselfConfig()) {
            config.oneselfConfig(factory);
        } else {
            PluginFollowCoreConfig followCoreConfig = new PluginFollowCoreConfig(
                    plugin.getMainApplicationContext()
            );
            MybatisConfiguration mybatisPlusConfiguration = followCoreConfig.getMybatisPlusConfiguration();
            factory.setDataSource(followCoreConfig.getDataSource());
            factory.setConfiguration(mybatisPlusConfiguration);
            Interceptor[] interceptor = followCoreConfig.getInterceptor();
            if (interceptor != null && interceptor.length > 0) {
                factory.setPlugins(interceptor);
            }
            DatabaseIdProvider databaseIdProvider = followCoreConfig.getDatabaseIdProvider();
            if (databaseIdProvider != null) {
                factory.setDatabaseIdProvider(databaseIdProvider);
            }
            LanguageDriver[] languageDriver = followCoreConfig.getLanguageDriver();
            if (languageDriver != null) {
                factory.setScriptingLanguageDrivers(languageDriver);
            }
            // 配置mybatis-plus私有的配置
            GlobalConfig globalConfig = mybatisPlusFollowCoreConfig(factory, plugin.getMainApplicationContext());
            config.reSetMainConfig(mybatisPlusConfiguration, globalConfig);
        }

        PluginResourceFinder pluginResourceFinder = new PluginResourceFinder(plugin);

        Class<?>[] aliasesClasses = pluginResourceFinder.getAliasesClasses(config.entityPackage());
        if (aliasesClasses != null && aliasesClasses.length > 0) {
            factory.setTypeAliases(aliasesClasses);
        }

        Resource[] xmlResource = pluginResourceFinder.getXmlResource(config.xmlLocationsMatch());
        if (xmlResource != null && xmlResource.length > 0) {
            factory.setMapperLocations(xmlResource);
        }
        ClassLoader defaultClassLoader = Resources.getDefaultClassLoader();
        try {
            Resources.setDefaultClassLoader(plugin.getPluginWrapper().getPluginClassLoader());
            SqlSessionFactory sqlSessionFactory = factory.getObject();
            if (sqlSessionFactory == null) {
                throw new Exception("Get mybatis-plus sqlSessionFactory is null");
            }
            SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
            MapperHandler mapperHandler = new MapperHandler();
            mapperHandler.processMapper(plugin, (holder, mapperClass) ->
                    mapperHandler.commonProcessMapper(holder, mapperClass, sqlSessionFactory, sqlSessionTemplate));
            DefaultListableBeanFactory beanFactory = plugin.getPluginApplicationContext().getDefaultListableBeanFactory();
            beanFactory.registerSingleton("sqlSessionFactory", sqlSessionFactory);
            beanFactory.registerSingleton("sqlSession", sqlSessionTemplate);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            Resources.setDefaultClassLoader(defaultClassLoader);
        }
    }

    @Override
    public void unRegistry(PluginInfo plugin) throws Exception {
        // 不做操作，直接通过关闭PluginApplicationContext完成注销。
    }

    private GlobalConfig mybatisPlusFollowCoreConfig(MybatisSqlSessionFactoryBean factory,
                                                     ApplicationContext mainApplicationContext) {
        MybatisPlusProperties plusProperties = mainApplicationContext.getBean(MybatisPlusProperties.class);

        GlobalConfig currentGlobalConfig = new GlobalConfig();
        currentGlobalConfig.setBanner(false);
        GlobalConfig globalConfig = plusProperties.getGlobalConfig();
        if (globalConfig != null) {
            currentGlobalConfig.setDbConfig(globalConfig.getDbConfig());
            currentGlobalConfig.setIdentifierGenerator(globalConfig.getIdentifierGenerator());
            currentGlobalConfig.setMetaObjectHandler(globalConfig.getMetaObjectHandler());
            currentGlobalConfig.setSqlInjector(globalConfig.getSqlInjector());
        }
        factory.setGlobalConfig(currentGlobalConfig);
        return currentGlobalConfig;
    }

    // TODO 临时放这里，先跑通mybatis-plus集成测试
    public static <T> T getObjectByInterfaceClass(Set<Object> sourceObject, Class<T> interfaceClass) {
        if (sourceObject == null || sourceObject.isEmpty()) {
            return null;
        }
        for (Object configSingletonObject : sourceObject) {
            Set<Class<?>> allInterfacesForClassAsSet = ClassUtils
                    .getAllInterfacesAsSet(configSingletonObject);
            if (allInterfacesForClassAsSet.contains(interfaceClass)) {
                return (T) configSingletonObject;
            }
        }
        return null;
    }
}
