package com.jeelowcode.service.infra.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.jeelowcode.tool.framework.common.util.object.BeanUtils;
import com.jeelowcode.tool.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.jeelowcode.tool.framework.mybatis.core.util.JdbcUtils;
import com.jeelowcode.service.infra.controller.vo.db.DataSourceConfigSaveReqVO;
import com.jeelowcode.service.infra.entity.DataSourceConfigDO;
import com.jeelowcode.service.infra.mapper.DataSourceConfigMapper;
import com.jeelowcode.service.infra.service.IDataSourceConfigService;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.jeelowcode.tool.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jeelowcode.service.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;
import static com.jeelowcode.service.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_OK;

/**
 * 数据源配置 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class DataSourceConfigServiceImpl implements IDataSourceConfigService {

    @Resource
    private DataSourceConfigMapper dataSourceConfigMapper;

    @Resource
    private DynamicDataSourceProperties dynamicDataSourceProperties;

    @Autowired
    private DynamicRoutingDataSource dynamicRoutingDataSource;

    @Override
    public void initDataSource2Pool(){
        List<DataSourceConfigDO> doList = dataSourceConfigMapper.selectList();
        if(CollectionUtil.isEmpty(doList)){
            return;
        }
        for(DataSourceConfigDO config:doList){
            try{
                validateConnectionOK(config);
                //加入到连接池
                DataSource dataSource = JdbcUtils.getDataSource(config.getUrl(),config.getUsername(),config.getPassword(),config.getDriverClass());
                dynamicRoutingDataSource.addDataSource(config.getDbCode(),dataSource);
            }catch (Exception e){

            }
        }

    }

    @Override
    public boolean codeIsExist(String code){
        if("master".equals(code)){
            return true;
        }else if("slave".equals(code)){
            return true;
        }


        LambdaQueryWrapperX<DataSourceConfigDO> wrapperX=new LambdaQueryWrapperX<>();
        wrapperX.eq(DataSourceConfigDO::getDbCode,code);
        DataSourceConfigDO dataSourceConfigDO = dataSourceConfigMapper.selectOne(wrapperX);
        return dataSourceConfigDO!=null;
    }

    @Override
    public Long createDataSourceConfig(DataSourceConfigSaveReqVO createReqVO) {
        DataSourceConfigDO config = BeanUtils.toBean(createReqVO, DataSourceConfigDO.class);
        validateConnectionOK(config);

        // 插入
        dataSourceConfigMapper.insert(config);
        //加入到连接池
        DataSource dataSource = JdbcUtils.getDataSource(config.getUrl(),config.getUsername(),config.getPassword(),config.getDriverClass());
        dynamicRoutingDataSource.addDataSource(config.getDbCode(),dataSource);
        // 返回
        return config.getId();
    }

    @Override
    public void updateDataSourceConfig(DataSourceConfigSaveReqVO updateReqVO) {
        // 校验存在
        validateDataSourceConfigExists(updateReqVO.getId());
        DataSourceConfigDO updateObj = BeanUtils.toBean(updateReqVO, DataSourceConfigDO.class);
        validateConnectionOK(updateObj);

        // 更新
        dataSourceConfigMapper.updateById(updateObj);

        //先移除再加入-加入到连接池
        if(isConnect(updateObj.getDbCode())){
            dynamicRoutingDataSource.removeDataSource(updateObj.getDbCode());
        }
        DataSource dataSource = JdbcUtils.getDataSource(updateObj.getUrl(),updateObj.getUsername(),updateObj.getPassword(),updateObj.getDriverClass());
        dynamicRoutingDataSource.addDataSource(updateObj.getDbCode(),dataSource);
    }

    @Override
    public void deleteDataSourceConfig(Long id) {
        // 校验存在
        validateDataSourceConfigExists(id);
        DataSourceConfigDO config = dataSourceConfigMapper.selectById(id);
        // 删除
        dataSourceConfigMapper.deleteById(id);
        //移除连接池
        if(config!=null && isConnect(config.getDbCode())){
            dynamicRoutingDataSource.removeDataSource(config.getDbCode());
        }
    }

    private void validateDataSourceConfigExists(Long id) {
        if (dataSourceConfigMapper.selectById(id) == null) {
            throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS);
        }
    }

    @Override
    public DataSourceConfigDO getDataSourceConfig(Long id) {
        // 如果 id 为 0，默认为 master 的数据源
        if (Objects.equals(id, DataSourceConfigDO.ID_MASTER)) {
            return buildMasterDataSourceConfig();
        }
        // 从 DB 中读取
        return dataSourceConfigMapper.selectById(id);
    }

    @Override
    public DataSourceConfigDO getDataSourceConfigByCode(String code){
        LambdaQueryWrapperX<DataSourceConfigDO> wrapperX=new LambdaQueryWrapperX<>();
        wrapperX.eq(DataSourceConfigDO::getDbCode,code);
        DataSourceConfigDO dataSourceConfigDO = dataSourceConfigMapper.selectOne(wrapperX);
        return dataSourceConfigDO;
    }


    @Override
    public List<DataSourceConfigDO> getDataSourceConfigList() {
        List<DataSourceConfigDO> result = dataSourceConfigMapper.selectList();
        // 补充 master 数据源
        result.add(0, buildMasterDataSourceConfig());
        return result;
    }

    @Override
    public void validateConnectionOK(DataSourceConfigDO config) {
        boolean success = JdbcUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword(),config.getDriverClass());
        if (!success) {
            throw exception(DATA_SOURCE_CONFIG_NOT_OK);
        }
    }

    private DataSourceConfigDO buildMasterDataSourceConfig() {
        String primary = dynamicDataSourceProperties.getPrimary();
        DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary);
        return new DataSourceConfigDO().setId(DataSourceConfigDO.ID_MASTER).setName(primary)
                .setDbCode(primary)
                .setUrl(dataSourceProperty.getUrl())
                .setUsername(dataSourceProperty.getUsername())
                .setPassword(dataSourceProperty.getPassword());
    }

    //是否连接
    @Override
    public boolean isConnect(String dbCode){
        Map<String, DataSource> dataSourceMap = dynamicRoutingDataSource.getDataSources();
        return dataSourceMap.containsKey(dbCode);
    }

}
