package com.its.api.config;

import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.hibernate.cfg.AvailableSettings;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.Objects;

@Slf4j
@ToString
@Configuration
@EnableJpaRepositories(
        basePackages= {"com.its.api.utis.repository"},
        entityManagerFactoryRef = "utisEntityManagerFactory",
        transactionManagerRef = "utisTransactionManager")
@MapperScan(basePackages="com.its.api.utis.repository.*", sqlSessionFactoryRef = "utisSqlSessionFactory")
@EnableTransactionManagement
public class DatabaseUtisConfig {

    @Value("${spring.utis-datasource.hikari.mapper-locations:classpath:mybatis/mapper/**/*.xml}")
    String mapperLocations;

    @PostConstruct
    private void init() {
        if (this.mapperLocations.trim().equals("")) {
            this.mapperLocations = "classpath:mybatis/mapper/**/*.xml";
        }
        log.info("mapperLocations: {}", this.mapperLocations);
        log.info("{}", this);
    }

    @Bean(name="utisDataSource")
    @ConfigurationProperties(prefix="spring.utis-datasource.hikari")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name="utisJpaProperties")
    @ConfigurationProperties(prefix="spring.utis-jpa")
    public JpaProperties jpaProperties() {
        return new JpaProperties();
    }

    @Bean(name="utisEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Autowired @Qualifier("utisDataSource") DataSource primaryDataSource,
            @Autowired @Qualifier("utisJpaProperties") JpaProperties jpaProperties) {

        jpaProperties.getProperties().put(AvailableSettings.HBM2DDL_AUTO, "validate");

        return builder
                .dataSource(primaryDataSource)
                .properties(jpaProperties.getProperties())
                .packages("com.its.api.utis.model.entity")
                .persistenceUnit("default")
                .build();
    }

    @Bean(name="utisTransactionManager")
    public PlatformTransactionManager transactionManager(
            @Autowired @Qualifier("utisEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager(Objects.requireNonNull(entityManagerFactory.getObject()));
        transactionManager.setNestedTransactionAllowed(true);
        return transactionManager;
    }

    @Bean(name="utisSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean(
            @Autowired @Qualifier("utisDataSource") DataSource dataSource, ApplicationContext applicationContext)
            throws Exception {
        log.info("mapperLocations: {}", this.mapperLocations);
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(applicationContext.getResources(this.mapperLocations));
        return factoryBean.getObject();
    }

    @Bean(name="utisSqlSession")
    public SqlSessionTemplate sqlSession(
            @Autowired @Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean(name="utisSqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(
            @Autowired @Qualifier("utisSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
