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

import com.airlock.iam.common.api.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.common.application.configuration.secretquestion.BasicSecretQuestionSettings;
import com.airlock.iam.core.misc.impl.authen.AuthenticationMethodFactory;
import com.airlock.iam.flow.api.application.configuration.dynamicactivation.DynamicStepActivationConfig;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.step.AbstractInteractiveFlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.InteractiveGotoTargetConfig;
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.NextActions;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.publicselfservice.domain.model.step.SecretQuestionsIdentityVerificationStep;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Map;

@Plugin(
   description = "<p>Public self-service flow step that verifies the user identity by asking secret questions that the user has to answer correctly for the flow to continue.</p><p>This is an <em>identity verification</em> step that differs from an \"approval\" step in the following ways: <ul><li>It doesn't fail with non-existing users or users without provisioned secret questions.</li>  <li>It implements stealth mode: if a user does not exist or cannot do public self-services for whatever reason, no error is returned, but any answers entered are rejected, so that the step can never be completed successfully.</li></ul>",
   displayName = "Secret Questions Identity Verification Step"
)
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control", "Advanced Settings"})
@NextActions(SecretQuestionsIdentityVerificationStep.SecretQuestionsStepAction.class)
public final class SecretQuestionsIdentityVerificationStepConfig extends AbstractInteractiveFlowStepConfig implements StepConfigForPublicSelfServiceFlows {
   @PluginProperty(description = "Settings related to secret questions.", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private BasicSecretQuestionSettings secretQuestionsSettings;
   @IntegerProperty(
      description = "Number of allowed failed attempts before the flow is aborted.",
      exampleValues = {0, 3, 5},
      minValue = 0,
      defaultValue = 1,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private int maxFailedAttempts;
   @StringProperty(
      description = "The identifier of the authentication method for this step. Since the authentication method is also the identifier for failed login attempts for this step, distinct identifiers must be chosen if multiple instances of the same step type are used to check different credentials (e.g., two password step instances check two different passwords).<p>Example: a system tracks two passwords per user. Password 1 is used in flow A, password 2 in flow B. These two steps must have distinct 'Authentication Method Ids', e.g. PASSWORD1 and PASSWORD2.</p>If only one identifier (e.g. \"PASSWORD\") is used, this may allow brute force attacks on the password as follows. Assuming an attacker knows the user's password 1, they get an unlimited number of attempts on flow B, as the 'PASSWORD' counter can repeatedly be reset to 0 by performing a successful login on flow A.<p>To prevent such attacks, use two different counters by setting the authentication methods, for example, to PASSWORD1 and PASSWORD2, respectively.</p>",
      displayName = "Authentication Method ID",
      defaultValue = "SECRET_QUESTIONS",
      maxLength = 23,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String authenticationMethodId;

   public AuthenticationMethod getAuthenticationMethodId() {
      return AuthenticationMethodFactory.authMethodFor(this.authenticationMethodId);
   }

   public BasicSecretQuestionSettings getSecretQuestionsSettings() {
      return this.secretQuestionsSettings;
   }

   public int getMaxFailedAttempts() {
      return this.maxFailedAttempts;
   }

   protected SecretQuestionsIdentityVerificationStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      List<InteractiveGotoTargetConfig> interactiveGotoTargets,
      List<DynamicStepActivationConfig> dynamicStepActivations,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      BasicSecretQuestionSettings secretQuestionSettings,
      int maxFailedAttempts,
      String authenticationMethodId
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.maxFailedAttempts = maxFailedAttempts;
      this.secretQuestionsSettings = secretQuestionSettings;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class SecretQuestionsIdentityVerificationStepConfigBuilder {
      @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 List<TagConfig> tagsOnSuccess;
      @SuppressFBWarnings(justification = "generated code")
      private StepIdConfig stepId;
      @SuppressFBWarnings(justification = "generated code")
      private List<InteractiveGotoTargetConfig> interactiveGotoTargets;
      @SuppressFBWarnings(justification = "generated code")
      private List<DynamicStepActivationConfig> dynamicStepActivations;
      @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 BasicSecretQuestionSettings secretQuestionSettings;
      @SuppressFBWarnings(justification = "generated code")
      private int maxFailedAttempts;
      @SuppressFBWarnings(justification = "generated code")
      private String authenticationMethodId;

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsIdentityVerificationStepConfig.SecretQuestionsIdentityVerificationStepConfigBuilder tagsOnSuccess(List<TagConfig> tagsOnSuccess) {
         this.tagsOnSuccess = tagsOnSuccess;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsIdentityVerificationStepConfig.SecretQuestionsIdentityVerificationStepConfigBuilder interactiveGotoTargets(
         List<InteractiveGotoTargetConfig> interactiveGotoTargets
      ) {
         this.interactiveGotoTargets = interactiveGotoTargets;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsIdentityVerificationStepConfig.SecretQuestionsIdentityVerificationStepConfigBuilder dynamicStepActivations(
         List<DynamicStepActivationConfig> dynamicStepActivations
      ) {
         this.dynamicStepActivations = dynamicStepActivations;
         return this;
      }

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsIdentityVerificationStepConfig.SecretQuestionsIdentityVerificationStepConfigBuilder secretQuestionSettings(
         BasicSecretQuestionSettings secretQuestionSettings
      ) {
         this.secretQuestionSettings = secretQuestionSettings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsIdentityVerificationStepConfig.SecretQuestionsIdentityVerificationStepConfigBuilder maxFailedAttempts(int maxFailedAttempts) {
         this.maxFailedAttempts = maxFailedAttempts;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsIdentityVerificationStepConfig.SecretQuestionsIdentityVerificationStepConfigBuilder authenticationMethodId(
         String authenticationMethodId
      ) {
         this.authenticationMethodId = authenticationMethodId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsIdentityVerificationStepConfig build() {
         return new SecretQuestionsIdentityVerificationStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            this.tagsOnSuccess,
            this.stepId,
            this.interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.secretQuestionSettings,
            this.maxFailedAttempts,
            this.authenticationMethodId
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "SecretQuestionsIdentityVerificationStepConfig.SecretQuestionsIdentityVerificationStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", interactiveGotoTargets="
            + this.interactiveGotoTargets
            + ", dynamicStepActivations="
            + this.dynamicStepActivations
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", secretQuestionSettings="
            + this.secretQuestionSettings
            + ", maxFailedAttempts="
            + this.maxFailedAttempts
            + ", authenticationMethodId="
            + this.authenticationMethodId
            + ")";
      }
   }
}
