package com.airlock.iam.authentication.application.configuration.targetapp;

import com.airlock.iam.authentication.application.configuration.processor.DefaultAuthenticationProcessorsConfig;
import com.airlock.iam.authentication.application.configuration.processor.LoginHistoryProcessorConfig;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.Environment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.ConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyConfigurationException;
import com.airlock.iam.plugin.api.valuereader.BooleanPropertyValueReader;
import com.airlock.iam.plugin.framework.misc.config.validation.RecursivePluginValidator;
import com.airlock.iam.plugin.framework.misc.config.validation.RecursivePluginValidator.ValidatorArguments;
import java.util.Arrays;
import java.util.Optional;

public final class ValidateNoLoginHistoryPlugins {
   private static final Class<?>[] LOGIN_HISTORY_FLOW_PROCESSOR_CONFIGS = new Class[]{LoginHistoryProcessorConfig.class};
   private static final Class<?>[] DEFAULT_AUTHENTICATION_PROCESSOR_CONFIGS = new Class[]{DefaultAuthenticationProcessorsConfig.class};

   private ValidateNoLoginHistoryPlugins() {
   }

   static void validateNoLoginHistoryPlugins(PluginConfiguration pc, ConfigurationEnvironment env, ConfigurationContext context, Environment valEnv) throws ConfigurationException {
      new RecursivePluginValidator(pc, env, context, valEnv).validate(ValidateNoLoginHistoryPlugins::validateNoLoginHistoryPlugins);
   }

   private static Optional<PropertyConfigurationException> validateNoLoginHistoryPlugins(ValidatorArguments arguments) {
      if (pluginIsALoginHistoryFlowProcessorConfig(arguments)) {
         return createPropertyConfigurationException(arguments);
      } else {
         return pluginIsADefaultAuthenticationProcessor(arguments) && writeLoginHistoryEnabled(arguments)
            ? createPropertyConfigurationException(arguments)
            : Optional.empty();
      }
   }

   private static Optional<PropertyConfigurationException> createPropertyConfigurationException(ValidatorArguments arguments) {
      return Optional.of(
         new PropertyConfigurationException(
            arguments.parent(),
            arguments.env(),
            arguments.context(),
            "A Login History Repository is required by one or more authentication flows because they either use a\nDefault Authentication Processor with the \"Write Login History\" flag enabled or a\n\"Custom Flow Processors\" plugin containing a \"Login History Processor\".",
            new String[]{arguments.propertyName()}
         )
      );
   }

   static boolean pluginIsALoginHistoryFlowProcessorConfig(ValidatorArguments arguments) {
      return Arrays.stream(LOGIN_HISTORY_FLOW_PROCESSOR_CONFIGS).map(Class::getName).anyMatch(c -> c.equals(arguments.pluginConfig().getPluginClassName()));
   }

   static boolean pluginIsADefaultAuthenticationProcessor(ValidatorArguments arguments) {
      return Arrays.stream(DEFAULT_AUTHENTICATION_PROCESSOR_CONFIGS).map(Class::getName).anyMatch(c -> c.equals(arguments.pluginConfig().getPluginClassName()));
   }

   private static boolean writeLoginHistoryEnabled(ValidatorArguments arguments) {
      try {
         return BooleanPropertyValueReader.effectiveBooleanPropertyValue(arguments.pluginConfig(), "writeLoginHistory", arguments.env(), arguments.context());
      } catch (ConfigurationException e) {
         throw new IllegalStateException(e);
      }
   }
}
