package com.airlock.iam.flow.shared.application.configuration.step;

import com.airlock.iam.common.api.application.configuration.valueprovider.StringValueProviderConfig;
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.shared.api.application.configuration.message.MtanMessageProviderConfig;
import com.airlock.iam.flow.shared.application.configuration.mtan.MtanOtpCheckConfigSpec;
import com.airlock.iam.flow.shared.application.configuration.valueprovider.MtanRegistrationNumberProviderConfig;
import com.airlock.iam.flow.shared.domain.model.step.mtan.MtanVerificationStep;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForAuthenticationFlows;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForSelfServiceFlows;
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.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
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 = "Flow step to verify the registration of an mTAN number of a previous user data edit step to be used as an authentication method. It sends an OTP to the number to be verified and optionally also verifies an IAK that was sent by letter. This step is typically followed by an \"Apply Changes Step\" with an \"Apply mTAN Registration Change\" handler, unless the number is already pre-registered and persisting is not needed. ",
   displayName = "mTAN Verification Step"
)
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control", "Advanced Settings"})
@NextActions(MtanVerificationStep.MtanVerificationStepActions.class)
public class MtanVerificationStepConfig extends AbstractInteractiveFlowStepConfig implements StepConfigForAuthenticationFlows, StepConfigForSelfServiceFlows {
   public static final String MTAN_VERIFICATION_STEP_DISPLAY_NAME = "mTAN Verification Step";
   @PluginProperty(description = "Defines the required settings for mTAN.", displayName = "mTAN Settings", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private MtanOtpCheckConfigSpec mtanSettings;
   @BooleanProperty(
      description = "<p>If enabled, IAK verification is required. Make sure that the configured mTAN Handler also supports IAK verification.</p><p>Typically, this is not needed if the step is configured in a migration subflow, but it is essential for security reasons if the step is used during authentication for new users that have not yet registered an mTAN number.</p>",
      displayName = "IAK Verification Required",
      optional = true,
      defaultValue = "true"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean iakVerificationRequired;
   @PluginProperty(description = "Provides the phone number to be verified.", optional = true, defaultValue = MtanRegistrationNumberProviderConfig.class)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private StringValueProviderConfig phoneNumberProvider;
   @PluginProperty(description = "Creates the message for the verification SMS.", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private MtanMessageProviderConfig messageProvider;
   @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 = "MTAN",
      maxLength = 23,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String authenticationMethodId;

   public MtanVerificationStepConfig(
      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,
      MtanOtpCheckConfigSpec mtanSettings,
      boolean iakVerificationRequired,
      StringValueProviderConfig phoneNumberProvider,
      MtanMessageProviderConfig messageProvider,
      String authenticationMethodId
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.mtanSettings = mtanSettings;
      this.iakVerificationRequired = iakVerificationRequired;
      this.phoneNumberProvider = phoneNumberProvider;
      this.messageProvider = messageProvider;
      this.authenticationMethodId = authenticationMethodId;
   }

   public MtanOtpCheckConfigSpec getMtanSettings() {
      return this.mtanSettings;
   }

   public boolean isIakVerificationRequired() {
      return this.iakVerificationRequired;
   }

   public StringValueProviderConfig getPhoneNumberProvider() {
      return this.phoneNumberProvider;
   }

   public MtanMessageProviderConfig getMessageProvider() {
      return this.messageProvider;
   }

   public String getAuthenticationMethodId() {
      return this.authenticationMethodId;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class MtanVerificationStepConfigBuilder {
      @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 ArrayList<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 MtanOtpCheckConfigSpec mtanSettings;
      @SuppressFBWarnings(justification = "generated code")
      private boolean iakVerificationRequired;
      @SuppressFBWarnings(justification = "generated code")
      private StringValueProviderConfig phoneNumberProvider;
      @SuppressFBWarnings(justification = "generated code")
      private MtanMessageProviderConfig messageProvider;
      @SuppressFBWarnings(justification = "generated code")
      private String authenticationMethodId;

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

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

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder 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 MtanVerificationStepConfig.MtanVerificationStepConfigBuilder clearTagsOnSuccess() {
         if (this.tagsOnSuccess != null) {
            this.tagsOnSuccess.clear();
         }

         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder interactiveGotoTarget(InteractiveGotoTargetConfig interactiveGotoTarget) {
         if (this.interactiveGotoTargets == null) {
            this.interactiveGotoTargets = new ArrayList<>();
         }

         this.interactiveGotoTargets.add(interactiveGotoTarget);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder interactiveGotoTargets(
         Collection<? extends InteractiveGotoTargetConfig> interactiveGotoTargets
      ) {
         if (interactiveGotoTargets == null) {
            throw new NullPointerException("interactiveGotoTargets cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder clearInteractiveGotoTargets() {
         if (this.interactiveGotoTargets != null) {
            this.interactiveGotoTargets.clear();
         }

         return this;
      }

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder mtanSettings(MtanOtpCheckConfigSpec mtanSettings) {
         this.mtanSettings = mtanSettings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder iakVerificationRequired(boolean iakVerificationRequired) {
         this.iakVerificationRequired = iakVerificationRequired;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder phoneNumberProvider(StringValueProviderConfig phoneNumberProvider) {
         this.phoneNumberProvider = phoneNumberProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig.MtanVerificationStepConfigBuilder messageProvider(MtanMessageProviderConfig messageProvider) {
         this.messageProvider = messageProvider;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public MtanVerificationStepConfig build() {
         List tagsOnSuccess = 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));
         };

         return new MtanVerificationStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            tagsOnSuccess,
            this.stepId,
            switch (this.interactiveGotoTargets == null ? 0 : this.interactiveGotoTargets.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.interactiveGotoTargets.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.interactiveGotoTargets));
            },
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.mtanSettings,
            this.iakVerificationRequired,
            this.phoneNumberProvider,
            this.messageProvider,
            this.authenticationMethodId
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "MtanVerificationStepConfig.MtanVerificationStepConfigBuilder(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
            + ", mtanSettings="
            + this.mtanSettings
            + ", iakVerificationRequired="
            + this.iakVerificationRequired
            + ", phoneNumberProvider="
            + this.phoneNumberProvider
            + ", messageProvider="
            + this.messageProvider
            + ", authenticationMethodId="
            + this.authenticationMethodId
            + ")";
      }
   }
}
