package com.airlock.iam.apipolicyservice.app.infrastructure;

import com.airlock.iam.apipolicyservice.application.configuration.ApiPolicyServiceAppConfig;
import com.airlock.iam.common.application.configuration.usertrail.UserTrailLogRepositoryConfig;
import com.airlock.iam.common.domain.model.usertrail.UserTrailLogRepository;
import com.airlock.iam.common.infrastructure.usertrail.UserTrailLogDatabaseAppender;
import com.airlock.iam.core.misc.plugin.config.activation.ConfigActivator;
import com.airlock.iam.core.misc.plugin.config.activation.ConfigurationRootContainer;
import com.airlock.iam.core.misc.plugin.config.activation.ConfigActivator.ContextHandling;
import com.airlock.iam.core.misc.util.config.ActiveLicenseEnvironment;
import com.airlock.iam.core.misc.util.config.Config;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.log.usertrail.UserTrailLogger;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.framework.misc.ioc.PluginLoader;
import com.airlock.iam.system.property.IamSystemProperties;
import com.airlock.iam.system.version.CurrentVersion;
import com.google.common.base.Suppliers;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.function.Supplier;

public final class Configuration implements ConfigurationRootContainer {
   private Map<ConfigurationContext, ApiPolicyServiceAppConfig> appsByContext = new HashMap<>();
   private final Supplier<ConfigActivator> configActivator = Suppliers.memoize(
      () -> new ConfigActivator(
         ApiPolicyServiceAppConfig.API_POLICY_SERVICE_CONFIG_IDENTIFIER,
         ContextHandling.ALL_CONTEXTS,
         IamSystemProperties.getActivatedConfigsDirectory(),
         ActiveLicenseEnvironment.activeLicenseManager()
      )
   );
   private static final ELogger LOG = ELoggerFactory.getLogger(Configuration.class);
   private static Configuration instance;
   private boolean licensed;

   public static Configuration getInstance() {
      if (instance == null) {
         throw new IllegalStateException("configuration not initialized");
      } else {
         return instance;
      }
   }

   public static void initialize() {
      instance = new Configuration();
      Config.setInstance(ApiPolicyServiceAppConfig.API_POLICY_SERVICE_CONFIG_IDENTIFIER, configurationContext -> {
         throw new UnsupportedOperationException("API Policy Service does not support sessions");
      });
      instance.initConfigActivator();
   }

   private Configuration() {
   }

   private void initConfigActivator() {
      this.getConfigActivator().loadPluginTreeAsync(instance);
      this.getConfigActivator().autoActivateOnConfigFileChanges();
   }

   public ApiPolicyServiceAppConfig getApiPolicyServiceAppConfig(ConfigurationContext context) {
      ApiPolicyServiceAppConfig app = this.appsByContext.get(context);
      return app == null ? this.appsByContext.get(ConfigurationContext.defaultContext()) : app;
   }

   public ConfigActivator getConfigActivator() {
      return this.configActivator.get();
   }

   public Set<ConfigurationContext> getAllApiPolicyServiceAppContexts() {
      return Set.copyOf(this.appsByContext.keySet());
   }

   public void setConfigurationRoots(Map<ConfigurationContext, Object> configRoots) {
      this.appsByContext = new HashMap<>();

      for (Entry<ConfigurationContext, Object> entry : configRoots.entrySet()) {
         this.appsByContext.put(entry.getKey(), (ApiPolicyServiceAppConfig)entry.getValue());
      }
   }

   public boolean isLicensed() {
      return this.licensed;
   }

   public void setLicensed(boolean licensed) {
      this.licensed = licensed;
   }

   public void activate(Map<ConfigurationContext, Object> configRoots) {
      if (Config.setActive(true)) {
         LOG.info(" ***** API POLICY SERVICE MODULE: ACTIVATING *****");
         LOG.info("Version: " + CurrentVersion.getDisplayString());
      }

      this.setConfigurationRoots(configRoots);
      this.initUserTrailLogAppenders();
   }

   public void deactivate() {
      if (Config.setActive(false)) {
         LOG.info(" ***** API POLICY SERVICE MODULE: DEACTIVATING *****");
         LOG.info("Version: " + CurrentVersion.getDisplayString());
         LOG.info("shutting down plugin instances (if any)");
         UserTrailLogger.unhookAllUserTrailLogAppenders();

         for (ConfigurationContext ctx : getInstance().getAllApiPolicyServiceAppContexts()) {
            PluginLoader.unloadPluginTree(getInstance().getApiPolicyServiceAppConfig(ctx));
         }

         LOG.debug(" ***** API POLICY SERVICE MODULE: INACTIVE *****");
      }
   }

   public static ApiPolicyServiceAppConfig get(ConfigurationContext configurationContext) {
      return getInstance().getApiPolicyServiceAppConfig(configurationContext);
   }

   private void initUserTrailLogAppenders() {
      UserTrailLogger.unhookAllUserTrailLogAppenders();

      for (Entry<ConfigurationContext, ApiPolicyServiceAppConfig> entry : this.appsByContext.entrySet()) {
         if (entry.getKey().isDefaultContext()) {
            this.initDefaultContextAppender(entry.getValue());
         } else {
            this.initAdditionalContextAppenders(entry.getValue(), entry.getKey());
         }
      }
   }

   private void initDefaultContextAppender(ApiPolicyServiceAppConfig rootConfig) {
      rootConfig.getUserTrailLogRepositoryConfig()
         .<UserTrailLogRepository>map(UserTrailLogRepositoryConfig::getRepository)
         .ifPresent(repository -> UserTrailLogger.defineUserTrailLogAppenderForDefaultContext(new UserTrailLogDatabaseAppender(repository)));
   }

   private void initAdditionalContextAppenders(ApiPolicyServiceAppConfig rootConfig, ConfigurationContext context) {
      rootConfig.getUserTrailLogRepositoryConfig()
         .<UserTrailLogRepository>map(UserTrailLogRepositoryConfig::getRepository)
         .ifPresent(repository -> UserTrailLogger.defineUserTrailLogAppenderForContext(context.getIdentifier(), new UserTrailLogDatabaseAppender(repository)));
   }
}
