package ru.nbch.indicsearchservice.config.datasource;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import ru.nbch.indicsearchservice.config.settings.ApplicationSettings;
import ru.nbch.indicsearchservice.util.typehandler.NullableLocalDateTimeTypeHandler;
import ru.nbch.indicsearchservice.util.typehandler.NullableLocalDateTypeHandler;

@Configuration
@MapperScan(
    basePackages = "ru.nbch.indicsearchservice.repository.bank",
    sqlSessionFactoryRef = "bankSqlSessionFactory"
)
@Profile("!test")
public class DataSourceConfig {
    private static final Logger log = LoggerFactory.getLogger(DataSourceConfig.class);

    private final ApplicationSettings settings;

    public DataSourceConfig(ApplicationSettings settings) {
        this.settings = settings;
    }

    @Bean(name = "bankDataSource")
    public DataSource dataSource() {
        log.info("Connecting to bank database...");
        HikariConfig config = new HikariConfig();
        config.setDriverClassName(settings.getDataSource().getDriver());
        config.setJdbcUrl(settings.getDataSource().getUrl());
        config.setUsername(settings.getDataSource().getUsername());
        config.setPassword(settings.getDataSource().getPassword());

        config.setConnectionTimeout(settings.getDataSource().getConnectionTimeout());
        config.setValidationTimeout(settings.getDataSource().getValidationTimeout());
        config.setIdleTimeout(settings.getDataSource().getIdleTimeout());
        config.setMaximumPoolSize(settings.getDataSource().getMaxPoolSize());
        config.setMaxLifetime(settings.getDataSource().getMaxLifetime());
        config.setKeepaliveTime(settings.getDataSource().getKeepAliveTime());

        config.setPoolName("bank-database-pool");

        return new HikariDataSource(config);
    }

    @Bean(name = "bankSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("bankDataSource") DataSource dataSource) throws Exception {
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.getTypeHandlerRegistry().register(LocalDate.class, JdbcType.DATE, new NullableLocalDateTypeHandler());
        configuration.getTypeHandlerRegistry().register(LocalDateTime.class, JdbcType.TIMESTAMP, new NullableLocalDateTimeTypeHandler());

        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactoryBean.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources("classpath:mappers/bank/*.xml")
        );
        return sqlSessionFactoryBean.getObject();
    }

    @Bean(name = "bankTransactionManager")
    @Primary
    public PlatformTransactionManager transactionManager(@Qualifier("bankDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
