package com.github.yizzuide.milkomeda.sundial;

import com.github.yizzuide.milkomeda.util.DataTypeConvertUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;


/**
 * 动态创建数据源工厂
 * @author jsq [email protected]
 * @since 3.4.0
 * Create at 2020/5/8
 */
@Slf4j
public class DataSourceFactory implements EnvironmentAware {

    @Resource
    private SundialProperties sundialProperties;

    public static String DEFAULT_DATASOURCE_PREFIX = "spring.datasource";

    private static final ConfigurationPropertyNameAliases ALIASES = new ConfigurationPropertyNameAliases();

    /**
     * 配置绑定
     */
    private Binder binder;

    @PostConstruct
    public void init() {
        DEFAULT_DATASOURCE_PREFIX = sundialProperties.getConfigPrefix();
        ALIASES.addAliases("url", "jdbc-url");
        ALIASES.addAliases("username", "user");
    }

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        binder = Binder.get(environment);
    }

    /**
     * 创建用户配置的数据源
     * @return DataSource
     * @throws Exception    创建异常
     */
    public DataSource createDataSource() throws Exception {
        return createDataSource(null);
    }

    /**
     * 创建数据源实例
     * @param dataSourceConf    数据源配置
     * @return DataSource
     * @throws Exception    创建异常
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public DataSource createDataSource(SundialProperties.Datasource dataSourceConf) throws Exception {
        Map dataSourceProperties = binder.bind(DEFAULT_DATASOURCE_PREFIX, Map.class).get();
        if (dataSourceConf != null) {
            Map<String, Object> dataSourceConfMap = DataTypeConvertUtil.beanToMap(dataSourceConf);
            dataSourceProperties.putAll(dataSourceConfMap);
        }
        DataSource dataSource = sundialProperties.getDatasourceType().newInstance();
        bind(dataSource, dataSourceProperties);
        return dataSource;
    }

    @SuppressWarnings("rawtypes")
    private void bind(DataSource result, Map properties) {
        ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
        Binder binder = new Binder(source.withAliases(ALIASES));
        binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
    }
}