package ru.micord.ervu.account_applications.db;

import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;

import org.jooq.ConnectionProvider;
import org.jooq.DSLContext;
import org.jooq.ExecuteListener;
import org.jooq.conf.Settings;
import org.jooq.impl.DefaultConfiguration;
import org.jooq.impl.DefaultDSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;

import ru.cg.webbpm.modules.database.api.ConfigurationProvider;
import ru.cg.webbpm.modules.database.api.provider.DslProvider;
import ru.cg.webbpm.modules.database.bean.config.Datasource;
import ru.cg.webbpm.modules.database.impl.data_source.DataSourceConfigReader;
import ru.cg.webbpm.modules.database.impl.data_source.DataSourceFactory;
import ru.cg.webbpm.modules.database.impl.data_source.HikariDataSourceFactory;
import ru.cg.webbpm.modules.database.impl.data_source.JndiDataSourceFactory;
import ru.cg.webbpm.modules.database.impl.provider.DefaultConnectionProvider;
import ru.cg.webbpm.modules.database.impl.provider.DslProviderImpl;
import ru.cg.webbpm.modules.database.impl.service.ProcessSqlService;
import ru.cg.webbpm.modules.resources.api.ResourceProvider;

@Configuration
public class DbConfiguration {
  @Value("${webbpm.db.query_timeout:120}")
  private Integer queryTimeout;
  @Autowired
  private ResourceProvider resourceProvider;
  @Autowired
  private ProcessSqlService processSqlService;

  @Autowired
  private ExecuteListener[] executeListeners;

  private final Map<String, DSLContext> dslContextMap = new HashMap<>();
  private final Map<String, ConnectionProvider> connectionProviderMap = new HashMap<>();

  @Bean
  public Datasource datasourceConfig() {
    return DataSourceConfigReader.read(resourceProvider);
  }

  @Bean(destroyMethod = "destroy")
  public DataSourceFactory dataSourceFactory(HikariDataSourceFactory hikariDataSourceFactory) {
    return new DataSourceFactory(hikariDataSourceFactory);
  }

  @Bean
  @Primary
  public DataSource datasource(DataSourceFactory dataSourceFactory) {
    return dataSourceFactory.createDataSource(datasourceConfig(),
        JndiDataSourceFactory::createDataSource
    );
  }

  @Bean
  public PlatformTransactionManager txManager(@Qualifier("datasource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }

  @Bean
  @Primary
  public TransactionAwareDataSourceProxy transactionAwareDataSource(DataSource dataSource) {
    return new TransactionAwareDataSourceProxy(dataSource);
  }

  @Bean
  @Primary
  public DefaultConnectionProvider connectionProvider(
      TransactionAwareDataSourceProxy transactionAwareDataSourceProxy) {
    return new DefaultConnectionProvider(transactionAwareDataSourceProxy, processSqlService,
        connectionProviderMap
    );
  }

  @Bean
  public Settings jooqSettings(DataSourceFactory dataSourceFactory) {
    Settings settings = new Settings();
    if (dataSourceFactory.dataSourceType() == DataSourceFactory.DataSourceType.HIKARI) {
      settings.setQueryTimeout(queryTimeout);
    }
    return settings;
  }

  @Bean
  @Primary
  public org.jooq.Configuration jooqConfiguration(ConnectionProvider connectionProvider,
      Settings jooqSettings) {
    DefaultConfiguration configuration = new DefaultConfiguration();
    configuration.setSQLDialect(datasourceConfig().getSqlDialect());
    jooqSettings = jooqSettings.withExecuteLogging(false);
    configuration.setSettings(jooqSettings);
    configuration.setConnectionProvider(connectionProvider);
    configuration.set(executeListeners);
    return configuration;
  }

  @Bean
  @Primary
  public DSLContext dsl(org.jooq.Configuration configuration) {
    return new DefaultDSLContext(configuration);
  }

  @Bean
  public ConfigurationProvider configurationProvider(
      PlatformTransactionManager platformTransactionManager) {
    return () -> platformTransactionManager;
  }

  @Bean
  public DslProvider dslProvider(@Qualifier("dsl") DSLContext dsl,
                                 DataSourceFactory dataSourceFactory,
                                 org.jooq.Configuration masterConfiguration,
                                 Datasource datasource) {
    return new DslProviderImpl(dsl, dataSourceFactory, masterConfiguration, datasource,
        processSqlService, connectionProviderMap, this.dslContextMap
    );
  }
}
