package com.bonus.canteen.core.customer.utils;

import com.bonus.canteen.core.common.utils.TenantContextHolder;
import com.bonus.canteen.core.dataset.rule.RoutingRule;
import com.bonus.canteen.core.dataset.tenant.TenantLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class Executors {
   private static final Logger log = LoggerFactory.getLogger(Executors.class);
   static TransactionTemplate transactionTemplate;
   static TransactionTemplate notSupportedTransactionTemplate;
   static TenantLoader tenantLoader;

   private Executors() {
   }

   public static <T> T readInTenant(long tenantId, Supplier<T> task) {
      return doInTenantInternal(notSupportedTransactionTemplate, tenantId, (status) -> {
         return task.get();
      });
   }

   public static void doInAllTenant(boolean continueOnError, Consumer<Long> task) {
      Iterator var2 = tenantLoader.listTenant().iterator();

      while(var2.hasNext()) {
         Long tenantId = (Long)var2.next();

         try {
            doInTenant(tenantId, (status) -> {
               task.accept(tenantId);
            });
         } catch (Exception var5) {
            log.error("Task error on tenant: {}", tenantId, var5);
            if (!continueOnError) {
               throw var5;
            }
         }
      }

   }

   public static void doInAllTenant(Consumer<Long> task) {
      doInAllTenant(true, task);
   }

   public static void doInTenant(long tenantId, Consumer<TransactionStatus> task) {
      doInTenantInternal(transactionTemplate, tenantId, (status) -> {
         task.accept(status);
         return null;
      });
   }

   public static <T> T doInTenant(long tenantId, Function<TransactionStatus, T> task) {
      return doInTenantInternal(transactionTemplate, tenantId, task);
   }

   public static <T> T doInTenant(long tenantId, Supplier<T> task) {
      return doInTenantInternal(transactionTemplate, tenantId, (status) -> {
         return task.get();
      });
   }

   private static <T> T doInTenantInternal(TransactionTemplate transactionTemplate, long tenantId, Function<TransactionStatus, T> task) {
      RoutingRule.Context contextBefore = RoutingRule.getContext().clone();
      Long tenantIdBefore = TenantContextHolder.getTenantId();

      Object var6;
      try {
         RoutingRule.reset();
         TenantContextHolder.setTenantId(tenantId);
         var6 = transactionTemplate.execute((status) -> {
            try {
               return task.apply(status);
            } catch (Throwable var3) {
               throw new RuntimeException(var3);
            }
         });
      } finally {
         RoutingRule.setContext(contextBefore);
         TenantContextHolder.setTenantId(tenantIdBefore);
      }

      return (T) var6;
   }

   public static <T> T readInSystem(Supplier<T> task) {
      return doInSystemInternal(notSupportedTransactionTemplate, (status) -> {
         return task.get();
      });
   }

   public static void doInSystem(Consumer<TransactionStatus> task) {
      doInSystemInternal(transactionTemplate, (status) -> {
         task.accept(status);
         return null;
      });
   }

   public static <T> T doInSystem(Function<TransactionStatus, T> task) {
      return doInSystemInternal(transactionTemplate, task);
   }

   public static <T> T doInSystem(Supplier<T> task) {
      return doInSystemInternal(transactionTemplate, (status) -> {
         return task.get();
      });
   }

   private static <T> T doInSystemInternal(TransactionTemplate transactionTemplate, Function<TransactionStatus, T> task) {
      RoutingRule.Context contextBefore = RoutingRule.getContext().clone();
      Long tenantIdBefore = TenantContextHolder.getTenantId();

      Object var4;
      try {
         RoutingRule.reset();
         RoutingRule.useSystem();
         var4 = transactionTemplate.execute((status) -> {
            try {
               return task.apply(status);
            } catch (Throwable var3) {
               throw new RuntimeException(var3);
            }
         });
      } finally {
         RoutingRule.setContext(contextBefore);
         TenantContextHolder.setTenantId(tenantIdBefore);
      }

      return (T) var4;
   }

   public static void doInGlobalTs(String database, boolean global, Runnable task) {
      doInGlobalTs(database, global, () -> {
         task.run();
         return null;
      });
   }

   public static <T> T doInGlobalTs(String database, boolean global, Supplier<T> task) {
      RoutingRule.Context contextBefore = RoutingRule.getContext().clone();
      Long tenantIdBefore = TenantContextHolder.getTenantId();

      Object var5;
      try {
         RoutingRule.reset();
         RoutingRule.useGlobalTs(database);
         if (global) {
            TenantContextHolder.clear();
         }

         var5 = notSupportedTransactionTemplate.execute((status) -> {
            try {
               return task.get();
            } catch (Throwable var3) {
               throw new RuntimeException(var3);
            }
         });
      } finally {
         RoutingRule.setContext(contextBefore);
         TenantContextHolder.setTenantId(tenantIdBefore);
      }

      return (T) var5;
   }
}
