package com.airlock.iam.admin.application.configuration;

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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Map;
import java.util.function.Supplier;

@SuppressFBWarnings("NM_SAME_SIMPLE_NAME_AS_SUPERCLASS")
public final class Configuration implements ConfigurationRootContainer {
   private static final ELogger LOG = ELoggerFactory.getLogger(Configuration.class);
   private static Configuration instance;
   private final Supplier<ConfigActivator> configActivator = Suppliers.memoize(
      () -> new ConfigActivator(
         Adminapp.ADMINAPP_CONFIG_IDENTIFIER,
         ContextHandling.DEFAULT_CONTEXT_ONLY,
         IamSystemProperties.getActivatedConfigsDirectory(),
         ActiveLicenseEnvironment.activeLicenseManager()
      )
   );
   private boolean licensed;
   private Adminapp adminapp;

   private Configuration() {
      Config.logAuditLoggerConfigFileHash();
   }

   public static synchronized Configuration getInstance() {
      if (instance == null) {
         instance = new Configuration();
      }

      return instance;
   }

   public static void initialize() {
      Config.setInstance(Adminapp.ADMINAPP_CONFIG_IDENTIFIER, configurationContext -> Adminapp.get().getSessionIdleTimeout());
      getInstance().initConfigActivator();
   }

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

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

   public Adminapp getAdminapp() {
      return this.adminapp;
   }

   private void setConfigurationRoots(Map<ConfigurationContext, Object> configRoots) {
      this.adminapp = (Adminapp)configRoots.get(ConfigurationContext.defaultContext());
   }

   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(" ***** ADMINAPP MODULE: ACTIVATING *****");
         LOG.info("Version: " + CurrentVersion.getDisplayString());
      }

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

   public void deactivate() {
      if (Config.setActive(false)) {
         LOG.info(" ***** ADMINAPP MODULE: DEACTIVATING *****");
         LOG.info("Version: " + CurrentVersion.getDisplayString());
         UserTrailLogger.unhookAllUserTrailLogAppenders();
         PluginLoader.unloadPluginTree(this.adminapp);
         LOG.debug(" ***** ADMINAPP MODULE: INACTIVE *****");
      }
   }

   public static boolean isUnlicensed() {
      return !getInstance().isLicensed();
   }

   private void initUserTrailLogAppenders() {
      UserTrailLogger.unhookAllUserTrailLogAppenders();
      this.initDefaultContextAppender(this.adminapp);
   }

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