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

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.application.configuration.devicetoken.DeviceTokenSettings;
import com.airlock.iam.flow.shared.domain.model.devicetoken.DeviceTokenVerificationStep;
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.license.PluginLicenseRestriction;
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 = "Verifies the user's identity based on a signed challenge. This step can be used as a first step in the flow,\nmeaning the user has not been identified, or after a user identifying step. It provides user enumeration protection."
)
@PluginLicenseRestriction(moduleAffiliation = "DeviceToken")
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control", "Advanced Settings"})
@NextActions(DeviceTokenVerificationStep.DeviceTokenVerificationStepActions.class)
public class DeviceTokenIdentityVerificationStepConfig extends AbstractInteractiveFlowStepConfig implements StepConfigForPublicSelfServiceFlows {
   @PluginProperty(description = "Device token settings used for this step.", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private DeviceTokenSettings deviceTokenSettings;
   @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 = "DEVICE_TOKEN",
      maxLength = 23,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String authenticationMethodId;

   protected DeviceTokenIdentityVerificationStepConfig(
      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,
      DeviceTokenSettings deviceTokenSettings,
      String authenticationMethodId
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.deviceTokenSettings = deviceTokenSettings;
      this.authenticationMethodId = authenticationMethodId;
   }

   public DeviceTokenSettings getDeviceTokenSettings() {
      return this.deviceTokenSettings;
   }

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

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class DeviceTokenIdentityVerificationStepConfigBuilder {
      @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 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 DeviceTokenSettings deviceTokenSettings;
      @SuppressFBWarnings(justification = "generated code")
      private String authenticationMethodId;

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

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

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

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

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

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

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

         return this;
      }

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

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

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public DeviceTokenIdentityVerificationStepConfig.DeviceTokenIdentityVerificationStepConfigBuilder deviceTokenSettings(
         DeviceTokenSettings deviceTokenSettings
      ) {
         this.deviceTokenSettings = deviceTokenSettings;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public DeviceTokenIdentityVerificationStepConfig build() {
         return new DeviceTokenIdentityVerificationStepConfig(
            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.interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.deviceTokenSettings,
            this.authenticationMethodId
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "DeviceTokenIdentityVerificationStepConfig.DeviceTokenIdentityVerificationStepConfigBuilder(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
            + ", deviceTokenSettings="
            + this.deviceTokenSettings
            + ", authenticationMethodId="
            + this.authenticationMethodId
            + ")";
      }
   }
}
