package com.bonus.core.data.dataset.rule;

import com.bonus.core.data.dataset.exception.ChooseDatasetException;
import com.bonus.core.data.dataset.rule.loader.RouterLoader;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RoutingRule {
   private static final Logger log = LoggerFactory.getLogger(RoutingRule.class);
   private static final ThreadLocal<Context> CONTEXT_THREAD_LOCAL = new InheritableThreadLocal<Context>() {
      @Override
      protected Context initialValue() {
         return new Context();
      }
   };
   private static RouterLoader routerLoader;
   private static final LoadingCache<Object, RouterLoader.Router> ROUTERS = CacheBuilder.newBuilder().build(new CacheLoader<Object, RouterLoader.Router>() {
      @Override
      public RouterLoader.Router load(Object key) {
         return RoutingRule.routerLoader.load(key);
      }
   });

   public static synchronized void setRouterLoader(RouterLoader routerLoader) {
      if (RoutingRule.routerLoader == null) {
         RoutingRule.routerLoader = routerLoader;
      }
   }

   public static void accessMaster() {
      ((Context)CONTEXT_THREAD_LOCAL.get()).setAccessMode(AccessMode.MASTER);
   }

   public static void preferAccessSlave() {
      ((Context)CONTEXT_THREAD_LOCAL.get()).setAccessMode(AccessMode.PREFER_SLAVE);
   }

   public static void resetAccess() {
      ((Context)CONTEXT_THREAD_LOCAL.get()).setAccessMode((AccessMode)null);
   }

   public static boolean isAccessMaster() {
      return ((Context)CONTEXT_THREAD_LOCAL.get()).getAccessMode() == AccessMode.MASTER;
   }

   public static Context getContext() {
      return (Context)CONTEXT_THREAD_LOCAL.get();
   }

   public static void setContext(Context context) {
      CONTEXT_THREAD_LOCAL.set(context);
   }

   public static void useSystem() {
      ((Context)CONTEXT_THREAD_LOCAL.get()).setDataSourceType(DataSourceType.SYSTEM);
   }

   public static void useGlobalTs(String database) {
      ((Context)CONTEXT_THREAD_LOCAL.get()).setDataSourceType(DataSourceType.GLOBAL_TS);
      ((Context)CONTEXT_THREAD_LOCAL.get()).setDatabase(database);
   }

   public static void useBusiness() {
      ((Context)CONTEXT_THREAD_LOCAL.get()).setDataSourceType(DataSourceType.BUSINESS);
   }

   public static void resetUse() {
      ((Context)CONTEXT_THREAD_LOCAL.get()).setDataSourceType((DataSourceType)null);
   }

   public static boolean isUseSystem() {
      return ((Context)CONTEXT_THREAD_LOCAL.get()).getDataSourceType() == DataSourceType.SYSTEM || routerLoader.getRouterKey() == null;
   }

   public static boolean isUseGlobalTs() {
      return ((Context)CONTEXT_THREAD_LOCAL.get()).getDataSourceType() == DataSourceType.GLOBAL_TS;
   }

   public static void reset() {
      CONTEXT_THREAD_LOCAL.remove();
   }

   public static RouterLoader.Router getRouter() throws ChooseDatasetException {
      Object routerKey = routerLoader.getRouterKey();

      try {
         return (RouterLoader.Router)ROUTERS.get(routerKey);
      } catch (Exception var2) {
         log.error("load router failed for key: {},reason: {}", routerKey, var2.getMessage());
         throw new ChooseDatasetException(var2.getMessage());
      }
   }

   public static class Context implements Cloneable {
      private DataSourceType dataSourceType;
      private AccessMode accessMode;
      private String database;

      public Context clone() {
         return new Context(this.dataSourceType, this.accessMode, this.database);
      }

      public DataSourceType getDataSourceType() {
         return this.dataSourceType;
      }

      public AccessMode getAccessMode() {
         return this.accessMode;
      }

      public String getDatabase() {
         return this.database;
      }

      public void setDataSourceType(final DataSourceType dataSourceType) {
         this.dataSourceType = dataSourceType;
      }

      public void setAccessMode(final AccessMode accessMode) {
         this.accessMode = accessMode;
      }

      public void setDatabase(final String database) {
         this.database = database;
      }

      public Context(final DataSourceType dataSourceType, final AccessMode accessMode, final String database) {
         this.dataSourceType = dataSourceType;
         this.accessMode = accessMode;
         this.database = database;
      }

      public Context() {
      }
   }

   public static enum AccessMode {
      MASTER,
      PREFER_SLAVE;

      // $FF: synthetic method
      private static AccessMode[] $values() {
         return new AccessMode[]{MASTER, PREFER_SLAVE};
      }
   }

   public static enum DataSourceType {
      SYSTEM,
      BUSINESS,
      GLOBAL_TS;

      // $FF: synthetic method
      private static DataSourceType[] $values() {
         return new DataSourceType[]{SYSTEM, BUSINESS, GLOBAL_TS};
      }
   }
}
