package com.muyu.cloud.common.many.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.muyu.cloud.common.many.datasource.constents.DatasourceContent;
import com.muyu.cloud.common.many.datasource.domain.model.DataSourceInfo;
import com.muyu.cloud.common.many.datasource.factory.DruidDataSourceFactory;
import com.muyu.cloud.common.many.datasource.role.DynamicDataSource;
import com.muyu.cloud.common.saas.domain.model.EntInfo;
import com.muyu.cloud.common.saas.exception.SaaSException;
import com.muyu.common.core.domain.Result;
import com.muyu.common.core.utils.SpringUtils;
import com.muyu.common.system.domain.SysUser;
import com.muyu.common.system.remote.RemoteUserService;
import lombok.extern.log4j.Log4j2;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: zi run
 * @Date 2024/9/20 14:52
 * @Description 多数据源
 */
@Log4j2
@Component
@AutoConfiguration(before = MybatisPlusAutoConfiguration.class)
public class ManyDataSource implements ApplicationRunner {

    /**
     * 在应用启动时执行，初始化数据源连接池。
     * 获取 DruidDataSourceFactory 和 DynamicDataSource 实例，
     * 遍历数据源信息列表，为每个数据源创建 Druid 数据源并存储到动态数据源中。
     * @param args 应用启动参数
     */
    @Override
    public void run(ApplicationArguments args) {
        DruidDataSourceFactory druidDataSourceFactory = SpringUtils.getBean(DruidDataSourceFactory.class);
        DynamicDataSource dynamicDataSource = SpringUtils.getBean(DynamicDataSource.class);
        dataSourceInfoList().stream()
                .map(entInfo -> DataSourceInfo.hostAndPortBuild(
                                entInfo.getEntCode(), entInfo.getIp(), entInfo.getPort()
                        )
                )
                .forEach(dataSourceInfo -> {
                    DruidDataSource druidDataSource = druidDataSourceFactory.create(dataSourceInfo);
                    dynamicDataSource.put(dataSourceInfo.getKey(), druidDataSource);
                    log.info("存储数据连接池为：key:{}",dataSourceInfo.getKey());
                });
    }

    /**
     * 获取数据源信息
     * @return 企业信息
     */
    private List<EntInfo> dataSourceInfoList(){
        RemoteUserService remoteUserService = SpringUtils.getBean(RemoteUserService.class);
        Result<List<SysUser>> entListResult = remoteUserService.entList();
        if (entListResult==null){
            throw new SaaSException("saas远调数据源错误");
        }
        List<SysUser> data = entListResult.getData();
        if (entListResult.getCode() == Result.SUCCESS && data != null){
            return data.stream()
                    .map(d -> EntInfo.builder()
                            .entCode(d.getDatabaseName())
                            .ip(DatasourceContent.IP)
                            .port(DatasourceContent.PORT)
                            .build()
                    )
                    .toList();
        }else {
            log.error("远调数据源错误,远调数据为：{}", JSON.toJSONString(data));
            return null;
        }
    }

    /**
     * 创建一个 DynamicDataSource Bean，配置多个数据源。
     * 从数据源信息列表中获取配置，并使用 DruidDataSourceFactory 创建数据源实例。
     * @param druidDataSourceFactory 创建 Druid 数据源的工厂
     * @return 配置好的 DynamicDataSource 实例
     */
    @Bean
    public DynamicDataSource dynamicDataSource(DruidDataSourceFactory druidDataSourceFactory) {
        Map<Object, Object> dataSourceMap = dataSourceInfoList().stream()
                .map(entInfo -> DataSourceInfo.hostAndPortBuild(
                                entInfo.getEntCode(),
                                entInfo.getIp(),
                                entInfo.getPort()
                        )
                )
                .collect(Collectors.toMap(
                        dataSourceInfo -> dataSourceInfo.getKey(),
                        dataSourceInfo -> druidDataSourceFactory.create(dataSourceInfo)
                ));

        //设置动态数据源
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        dynamicDataSource.setDefineTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }

    /**
     * 创建 SqlSessionFactory Bean，用于 MyBatis 数据库操作。
     * 该方法接收动态数据源并配置 SqlSessionFactory，返回
     * 用于与数据库交互的 SqlSessionFactory 实例。
     * @param dataSource 动态数据源
     * @return 配置好的 SqlSessionFactory 实例
     * @throws Exception 可能抛出的异常
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }
}
