package com.bonus.core.data.dataset;

import com.bonus.core.data.dataset.rule.RoutingRule;
import com.bonus.core.data.dataset.rule.loader.RouterLoader;
import com.bonus.core.data.dataset.tx.XDataSourceTransactionManager;
import com.bonus.core.data.dataset.tx.XJdbcTransactionManager;
import com.bonus.core.tenant.TenantLoader;
import com.google.common.collect.Sets;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;

//@AutoConfiguration(
//   before = {DataSourceAutoConfiguration.class}
//)
//@EnableConfigurationProperties({RoutingDataSourceProperties.class})
@Configuration
public class RoutingDataSourceAutoConfiguration implements ApplicationContextAware {
   private static ApplicationContext applicationContext;

   public RoutingDataSourceAutoConfiguration(ObjectProvider<RouterLoader> routerLoaderProvider,
                                             ObjectProvider<TenantLoader> tenantLoaderObjectProvider
                                             ) {
      RoutingRule.setRouterLoader(routerLoaderProvider.getIfAvailable(() -> {
         return (key) -> {
            return null;
         };
      }));
      Executors.tenantLoader = tenantLoaderObjectProvider.getIfAvailable(() -> {
         return () -> {
            return Sets.newHashSetWithExpectedSize(0);
         };
      });
//      DatasetMigration.redissonClient = redissonClient;
   }

//   @ConditionalOnMissingBean
//   @Bean
//   public RoutingDataSource routingDataSource(RoutingDataSourceProperties routingDataSourceProperties, ObjectProvider<DataSourceLoader> dataSourceLoaderProviderLoaderProvider, ObjectProvider<MigrationProvider> migrationProviderObjectProvider, ObjectProvider<List<AbstractDataMigrator>> dataMigrateObjectProvider) {
//      RoutingDataSource routingDataSource = new RoutingDataSource(routingDataSourceProperties.systemDatasource(), routingDataSourceProperties.globalTsDatasource(), routingDataSourceProperties.businessDatasource(), routingDataSourceProperties.getDynamicBusinessConfig(), (DataSourceLoader)dataSourceLoaderProviderLoaderProvider.getIfAvailable(() -> {
//         return null;
//      }));
//      DatasetMigration.routingDataSource = routingDataSource;
//      DatasetMigration.migrationProvider = (MigrationProvider)migrationProviderObjectProvider.getIfUnique(DefaultMigrationProvider::new);
//      DatasetMigration.locationRefDataMigrates = dataMigrateObjectProvider.getIfAvailable() == null ? Collections.emptyMap() : (Map)((List)dataMigrateObjectProvider.getIfAvailable()).stream().collect(Collectors.groupingBy(AbstractDataMigrator::getLocation));
//      return routingDataSource;
//   }
//
//   @Bean
//   @ConditionalOnMissingBean({TransactionManager.class})
//   public DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
//      return this.newTransactionManager(environment, dataSource, transactionManagerCustomizers);
//   }

   private DataSourceTransactionManager newTransactionManager(Environment environment, DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
      DataSourceTransactionManager transactionManager = this.createTransactionManager(environment, dataSource);
      transactionManagerCustomizers.ifAvailable((customizers) -> {
         customizers.customize(transactionManager);
      });
      return transactionManager;
   }

   private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
      return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
              ? new XJdbcTransactionManager(dataSource) : new XDataSourceTransactionManager(dataSource);
   }

   @Bean
   @Primary
   public TransactionTemplate transactionTemplate(DataSourceTransactionManager transactionManager) {
      TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
      transactionTemplate.setPropagationBehavior(3);
      transactionTemplate.setIsolationLevel(2);
      transactionTemplate.setTimeout(transactionManager.getDefaultTimeout());
      Executors.transactionTemplate = transactionTemplate;
      return transactionTemplate;
   }

   @Bean
   public TransactionTemplate notSupportedTransactionTemplate(Environment environment, DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
      DataSourceTransactionManager transactionManager = this.newTransactionManager(environment, dataSource, transactionManagerCustomizers);
      transactionManager.setTransactionSynchronization(2);
      TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
      transactionTemplate.setPropagationBehavior(4);
      transactionTemplate.setTimeout(transactionManager.getDefaultTimeout());
      Executors.notSupportedTransactionTemplate = transactionTemplate;
      return transactionTemplate;
   }

   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      RoutingDataSourceAutoConfiguration.applicationContext = applicationContext;
   }

   public static ApplicationContext getApplicationContext() {
      return applicationContext;
   }
}
