package org.matrix.actuators.datasource;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Set;

/**
 * IDataSourceServiceImpl.
 *
 * @author Matrix <xhyrzldf@gmail.com>
 * @since 2022/1/19 at 5:47 PM
 * Suffering is the most powerful teacher of life.
 */
@Slf4j
@Service
public class IDataSourceServiceImpl implements IDataSourceService {

    private final DataSource dataSource;
    private final DefaultDataSourceCreator dataSourceCreator;

    public IDataSourceServiceImpl(DataSource dataSource, DefaultDataSourceCreator dataSourceCreator) {
        this.dataSource = dataSource;
        this.dataSourceCreator = dataSourceCreator;
    }

    /**
     * 获得当前线程正在使用的数据源
     *
     * @return 当前线程正在使用的数据源
     */
    @Override
    public DataSource peek() {
        String dsName = DynamicDataSourceContextHolder.peek();
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        return ds.getDataSources().get(dsName);
    }

    /**
     * 用于测试是否可以连接该目标数据源
     *
     * @param dataSourceDTO 用于连接目标数据源的参数对象
     * @return 是否可以连通该数据源
     */
    @Override
    public boolean testConnection(DataSourceDTO dataSourceDTO) throws SQLException {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        String url = dataSourceDTO.getUrl();
        String username = dataSourceDTO.getUsername();
        String password = dataSourceDTO.getPassword();
        Connection conn = null;
        DriverManager.getConnection(url, username, password);
        return true;
    }

    /**
     * 切换回主数据源
     *
     * @return 是否切换成功
     */
    @Override
    public boolean switchMainDataSource() {
        DynamicDataSourceContextHolder.push("master");
        return true;
    }

    /**
     * 将当前使用的数据替换为参数提供的数据源
     * 如果还没有该数据则会添加,如果已经有了,则不会添加
     *
     * @param dto 数据源信息
     * @return 当前存在的数据源集合
     */
    @Override
    public Set<String> switchDataSource(DataSourceDTO dto) {
        // todo 该方法需要添加线程安全保护
        Set<String> currentDataSources = add(dto);
        DynamicDataSourceContextHolder.push(dto.getPoolName());
        return currentDataSources;
    }

    /**
     * 添加数据源,如果已经存在同名的数据源了,则不会添加
     *
     * @param dto 数据源信息
     * @return 数据源名称的集合, 用于 DynamicDataSourceContextHolder.push("数据源名称") 这样的代码中
     */
    @Override
    public Set<String> add(DataSourceDTO dto) {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        if (ds.getDataSources().containsKey(dto.getPoolName())) {
            return ds.getDataSources().keySet();
        } else {
            return addDataSource(dto);
        }
    }

    /**
     * 添加数据源,如果已经存在同名的数据源了,则会覆盖
     *
     * @param dto 数据源信息
     * @return 数据源名称的集合, 用于 DynamicDataSourceContextHolder.push("数据源名称") 这样的代码中
     */
    @Override
    public Set<String> addAndUpdate(DataSourceDTO dto) {
        return addDataSource(dto);
    }

    /**
     * 获取当前所有数据源
     *
     * @return 数据源名称的集合, 用于 DynamicDataSourceContextHolder.push("数据源名称") 这样的代码中
     */
    @Override
    public Set<String> now() {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        return ds.getDataSources().keySet();
    }

    /**
     * 删除数据源
     *
     * @param name 数据源名称
     * @return 删除信息
     */
    @Override
    public String remove(String name) {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        ds.removeDataSource(name);
        return "删除成功";
    }

    private Set<String> addDataSource(DataSourceDTO dto) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(dto, dataSourceProperty);
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
        ds.addDataSource(dto.getPoolName(), dataSource);
        return ds.getDataSources().keySet();
    }
}
