package com.airlock.iam.publicselfservice.application.configuration.steps;

import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.step.AbstractFlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.NonInteractiveStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.StepIdConfig;
import com.airlock.iam.flow.api.application.configuration.tag.TagConfig;
import com.airlock.iam.flow.api.application.configuration.valueprovider.ValueMapProviderConfig;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForPublicSelfServiceFlows;
import com.airlock.iam.plugin.api.annotation.config.Groups;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.StringListProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Plugin(
   description = "<p>This step unlocks a user in the following cases:</p><ul><li>No 'Max Number of Unlocks' limit is configured in the 'Public Self-Service Flow Settings' configuration</li><li>The 'Max Number of Unlocks' limit configured in the 'Public Self-Service Flow Settings' has not yet been exceeded by the current user.</li></ul><p>Note that in general, locked users are not allowed to perform a public self-service, unless custom restrictions are configured to allow public self-services for certain locked users (see Locked User Restriction). If this step is performed by a locked user, his failed logins for the configured authentication methods are reset. Otherwise, the failed logins are not reset.</p><p><b>Security Notice 1:</b> This step should be the last step in the flow and must always come <em>after</em> at least one credential check step (e.g. email OTP). Precondition tags can help ensuring that only users that successfully completed a previous step are unlocked.</p><p><b>Security Notice 2:</b> Automatically unlocking users can have unexpected security implications. Unlocking users that are locked for <em>any reason other than too many failed password attempts</em> is potentially a security risk, as it might allow brute-force attacks on the second authentication factor.</p> <p><b>Security Notice 3:</b> If users are unlocked with proof of only one verified factor, the number of unlocks must be limited in the 'Public Self-Service Flow Settings' plugin configuration. Otherwise bruteforce attacks on one factor are possible, lowering the security of a two login-factor setup to a similar one as a one-factor setup.</p>",
   displayName = "Unlock User Step (Public Self-Service)"
)
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control"})
@PluginLicenseRestriction(moduleAffiliation = "UnlockSelfService")
public class PublicSelfServiceUnlockUserStepConfig extends AbstractFlowStepConfig implements NonInteractiveStepConfig, StepConfigForPublicSelfServiceFlows {
   public static final String UNLOCK_USER_STEP_DISPLAY_NAME = "Unlock User Step (Public Self-Service)";
   @BooleanProperty(
      description = "Reset the failed login counters for the HTML Loginapp to zero on unlock.",
      defaultValue = "false",
      displayName = "Reset Failed Logins (HTML Loginapp)",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean resetFailedLoginsForHtmlLoginapp;
   @StringListProperty(
      description = "Reset the failed login counters for specified authentication methods to zero on unlock. <b>Security Warning</b>: resetting counters for authentication methods other than \"PASSWORD\" might allow brute-forcing of the second authentication factor.",
      defaultValue = "PASSWORD",
      exampleElements = "PASSWORD",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private List<String> failedAttemptsCountersToReset;

   protected PublicSelfServiceUnlockUserStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      boolean resetFailedLoginsForHtmlLoginapp,
      List<String> failedAttemptsCountersToReset
   ) {
      super(skipCondition, preCondition, requiresActivation, tagsOnSuccess, stepId, onFailureGotos, customResponseAttributes, customFailureResponseAttributes);
      this.resetFailedLoginsForHtmlLoginapp = resetFailedLoginsForHtmlLoginapp;
      this.failedAttemptsCountersToReset = failedAttemptsCountersToReset;
   }

   public boolean isResetFailedLoginsForHtmlLoginapp() {
      return this.resetFailedLoginsForHtmlLoginapp;
   }

   public List<String> getFailedAttemptsCountersToReset() {
      return this.failedAttemptsCountersToReset;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder builder() {
      return new PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public PublicSelfServiceUnlockUserStepConfig() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class PublicSelfServiceUnlockUserStepConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig skipCondition;
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig preCondition;
      @SuppressFBWarnings(justification = "generated code")
      private boolean requiresActivation;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<TagConfig> tagsOnSuccess;
      @SuppressFBWarnings(justification = "generated code")
      private StepIdConfig stepId;
      @SuppressFBWarnings(justification = "generated code")
      private Map<String, StepIdConfig> onFailureGotos;
      @SuppressFBWarnings(justification = "generated code")
      private List<ValueMapProviderConfig> customResponseAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private List<ValueMapProviderConfig> customFailureResponseAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private boolean resetFailedLoginsForHtmlLoginapp;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> failedAttemptsCountersToReset;

      @SuppressFBWarnings(justification = "generated code")
      PublicSelfServiceUnlockUserStepConfigBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder skipCondition(FlowConditionConfig skipCondition) {
         this.skipCondition = skipCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder preCondition(FlowConditionConfig preCondition) {
         this.preCondition = preCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder requiresActivation(boolean requiresActivation) {
         this.requiresActivation = requiresActivation;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder tagOnSuccess(TagConfig tagOnSuccess) {
         if (this.tagsOnSuccess == null) {
            this.tagsOnSuccess = new ArrayList<>();
         }

         this.tagsOnSuccess.add(tagOnSuccess);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder tagsOnSuccess(Collection<? extends TagConfig> tagsOnSuccess) {
         if (tagsOnSuccess == null) {
            throw new NullPointerException("tagsOnSuccess cannot be null");
         }

         if (this.tagsOnSuccess == null) {
            this.tagsOnSuccess = new ArrayList<>();
         }

         this.tagsOnSuccess.addAll(tagsOnSuccess);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder clearTagsOnSuccess() {
         if (this.tagsOnSuccess != null) {
            this.tagsOnSuccess.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder stepId(@Nullable StepIdConfig stepId) {
         this.stepId = stepId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder onFailureGotos(Map<String, StepIdConfig> onFailureGotos) {
         this.onFailureGotos = onFailureGotos;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder customResponseAttributes(
         List<ValueMapProviderConfig> customResponseAttributes
      ) {
         this.customResponseAttributes = customResponseAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder customFailureResponseAttributes(
         List<ValueMapProviderConfig> customFailureResponseAttributes
      ) {
         this.customFailureResponseAttributes = customFailureResponseAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder resetFailedLoginsForHtmlLoginapp(
         boolean resetFailedLoginsForHtmlLoginapp
      ) {
         this.resetFailedLoginsForHtmlLoginapp = resetFailedLoginsForHtmlLoginapp;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder failedAttemptsCountersToReset(
         List<String> failedAttemptsCountersToReset
      ) {
         this.failedAttemptsCountersToReset = failedAttemptsCountersToReset;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUnlockUserStepConfig build() {
         return new PublicSelfServiceUnlockUserStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            switch (this.tagsOnSuccess == null ? 0 : this.tagsOnSuccess.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.tagsOnSuccess.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.tagsOnSuccess));
            },
            this.stepId,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.resetFailedLoginsForHtmlLoginapp,
            this.failedAttemptsCountersToReset
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "PublicSelfServiceUnlockUserStepConfig.PublicSelfServiceUnlockUserStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", resetFailedLoginsForHtmlLoginapp="
            + this.resetFailedLoginsForHtmlLoginapp
            + ", failedAttemptsCountersToReset="
            + this.failedAttemptsCountersToReset
            + ")";
      }
   }
}
