package com.airlock.iam.transactionapproval.application.configuration.airlock2fa;

import com.airlock.iam.factor.application.configuration.airlock2fa.Airlock2FASettings;
import com.airlock.iam.factor.application.configuration.airlock2fa.validation.Airlock2FAShortLivedQrCodeConfigValidator;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAShortLivedOnlineQrCodeSettings;
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.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.api.domain.model.step.StepAction;
import com.airlock.iam.flow.api.domain.model.step.StepError;
import com.airlock.iam.flow.shared.api.application.configuration.message.Airlock2FAMessageProviderConfig;
import com.airlock.iam.flow.shared.application.configuration.airlock2fa.Airlock2FAApprovalStepConfig;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAApprovalStep.Airlock2FAApprovalStepActions;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAApprovalStep.Airlock2FAApprovalStepErrors;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForTransactionApprovalFlows;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
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.BooleanProperty;
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.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 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;
import java.util.Optional;

@Plugin(
   description = "A flow step to perform transaction approval using Airlock 2FA.<p>The transaction data will be displayed on the Airlock 2FA app, where the user can verify the data and approve the transaction if satisfied.</p>",
   displayName = "Airlock 2FA Transaction Approval Step"
)
@PluginLicenseRestriction(moduleAffiliation = "Airlock2FA")
@Groups(
   openGroups = "Basic Settings",
   groupSequence = {"Basic Settings", "Short-Lived Online QR Code Settings", "Mobile Only Settings", "Tags/Guards", "Flow Control"}
)
@NextActions(
   {
         Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepActions.Actions.class,
         com.airlock.iam.flow.shared.application.configuration.airlock2fa.DefaultAirlock2FAApprovalStepActions.Actions.class
   }
)
public final class Airlock2FATransactionApprovalStepConfig extends Airlock2FAApprovalStepConfig implements StepConfigForTransactionApprovalFlows {
   @PluginProperty(description = "Creates the message for transaction approval that will be displayed on the user's device.", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private Airlock2FAMessageProviderConfig messageProvider;
   @BooleanProperty(
      displayName = "Enable Push-to-All",
      description = "If Push-to-All for One-Touch is enabled, device selection is never required for One-Touch. Push notifications are sent to all devices of a user and approval can be given on any of the devices.<p>The combination of Push-to-All and \"Lock User on Fraud\" could have undesired effects, because users might report fraud in legitimate use-cases.</p><p>If an AuthTokenId is provided, notifications are only sent to the device specified in the AuthTokenId.</p>",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean enablePushToAll;
   @BooleanProperty(
      displayName = "Enable Short-Lived Online QR Codes",
      description = "<p>Whether to enable short-lived Online QR Codes. Unlike regular Online QR Codes, these are refreshed regularly, allowing for shorter individual validities.</p>\n<p>Shorter validities enhance security, since forwarding a QR code to victims and tricking them to scan the QR code becomes more difficult if the available time window is small.</p>",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Short-Lived Online QR Code Settings")
   private boolean enableShortLivedOnlineQrCodes;
   @IntegerProperty(
      displayName = "QR Code Validity [s]",
      description = "<p>The maximum amount of time in seconds for which an Online QR Code is valid after it is first displayed to\nthe end user (ignoring latency). This duration includes the time defined for the validity overlap.\nIt only limits the time for scanning the QR code, not for the confirmation or approval afterwards.</p>\n<p><strong> Security Notice: The validity duration represents the attack window. Choosing a small validity makes\nattacks more difficult, in cases where an attacker attempts to forward a QR code to a victim for scanning.</strong></p>\n<p>This setting is only active if short-lived Online QR Codes are enabled.</p>",
      optional = true,
      defaultValue = 10,
      minValue = 3
   )
   @GroupProperty(groupIdentifier = "Short-Lived Online QR Code Settings")
   private int shortLivedQrCodeValidity;
   @IntegerProperty(
      displayName = "QR Code Validity Overlap [s]",
      description = "<p>Defines the duration in seconds during which the previously displayed QR Code is still valid after being\nreplaced by the next QR code in sequence.</p>\n<p>This provides time for pending requests to complete and ensures that a valid QR code is displayed at every\nmoment in time, provided that there are no network or performance issues.</p>\n<p>The validity overlap must meet the following criteria:</p>\n<ul>\n<li>It must be smaller than half the overall validity of the QR code.</li>\n<li>It must be larger than the Loginapp UI polling interval (1s) plus the network latency (IAM backend &rarr;\nLoginapp UI plus Mobile Device &rarr; Futurae Backend).<br/>\nNote that the polling interval may differ for custom user interfaces.</li>\n</ul>\n<p>This setting is only active if short-lived Online QR Codes are enabled.</p>",
      optional = true,
      defaultValue = 3,
      minValue = 2
   )
   @GroupProperty(groupIdentifier = "Short-Lived Online QR Code Settings")
   private int shortLivedQrCodeValidityOverlap;
   @IntegerProperty(
      displayName = "Session Timeout [s]",
      description = "<p>Maximum duration in seconds during which short-lived Online QR Codes are displayed until a session timeout occurs.</p>\n<p>This setting is used exclusively for short-lived Online QR Codes. It has no effect if short-lived Online QR Codes are disabled.</p>",
      optional = true,
      defaultValue = 60,
      minValue = 30,
      maxValue = 120,
      exampleValues = {30, 60, 90}
   )
   @GroupProperty(groupIdentifier = "Short-Lived Online QR Code Settings")
   private int shortLivedSessionTimeout;

   private Airlock2FATransactionApprovalStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      List<String> approvalFactors,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      List<InteractiveGotoTargetConfig> interactiveGotoTargets,
      List<DynamicStepActivationConfig> dynamicStepActivations,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      Airlock2FASettings airlock2faSettings,
      @Nullable String mobileOnlyRedirectUri,
      Airlock2FAMessageProviderConfig messageProvider,
      boolean enablePushToAll,
      boolean respectCooldownPeriod,
      boolean enableShortLivedOnlineQrCodes,
      int shortLivedQrCodeValidity,
      int shortLivedQrCodeValidityOverlap,
      int shortLivedSessionTimeout
   ) {
      super(
         skipCondition,
         preCondition,
         approvalFactors,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes,
         airlock2faSettings,
         mobileOnlyRedirectUri,
         respectCooldownPeriod
      );
      this.messageProvider = messageProvider;
      this.enablePushToAll = enablePushToAll;
      this.enableShortLivedOnlineQrCodes = enableShortLivedOnlineQrCodes;
      this.shortLivedQrCodeValidity = shortLivedQrCodeValidity;
      this.shortLivedQrCodeValidityOverlap = shortLivedQrCodeValidityOverlap;
      this.shortLivedSessionTimeout = shortLivedSessionTimeout;
   }

   @ConfigurationValidator(localOnly = true)
   public static void validateShortLivedQrCodeSettings(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext ctx, Environment vEnv) throws ConfigurationException {
      Airlock2FAShortLivedQrCodeConfigValidator.runShortLivedSettingsValidation("shortLivedSessionTimeout", Optional.of("approvalFactors"), config, env, ctx);
   }

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

   public boolean isEnablePushToAll() {
      return this.enablePushToAll;
   }

   public Optional<Airlock2FAShortLivedOnlineQrCodeSettings> getShortLivedOnlineQrCodeSettings() {
      return this.enableShortLivedOnlineQrCodes
         ? Optional.of(
            Airlock2FAShortLivedOnlineQrCodeSettings.shortLivedOnlineQrCodeSettings(
               this.shortLivedQrCodeValidity, this.shortLivedQrCodeValidityOverlap, this.shortLivedSessionTimeout
            )
         )
         : Optional.empty();
   }

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

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

   public static final class Airlock2FATransactionApprovalStepActions implements Airlock2FAApprovalStepActions {
      public StepAction pollingRequired() {
         return com.airlock.iam.flow.shared.application.configuration.airlock2fa.DefaultAirlock2FAApprovalStepActions.Actions.AIRLOCK_2FA_POLLING_REQUIRED;
      }

      public StepAction qrCodeOtpRequired() {
         return Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepActions.Actions.AIRLOCK_2FA_OTP_REQUIRED;
      }

      public StepAction pollingOrOfflineRequired() {
         return Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepActions.Actions.AIRLOCK_2FA_POLLING_OR_OTP_REQUIRED;
      }

      public StepAction deviceChoiceRequired() {
         return com.airlock.iam.flow.shared.application.configuration.airlock2fa.DefaultAirlock2FAApprovalStepActions.Actions.AIRLOCK_2FA_DEVICE_CHOICE_REQUIRED;
      }

      public StepAction mobileOnlyChallengeRetrievalRequired() {
         return com.airlock.iam.flow.shared.application.configuration.airlock2fa.DefaultAirlock2FAApprovalStepActions.Actions.AIRLOCK_2FA_MOBILE_ONLY_CHALLENGE_RETRIEVAL_REQUIRED;
      }

      public StepAction passcodeRequired() {
         return com.airlock.iam.flow.shared.application.configuration.airlock2fa.DefaultAirlock2FAApprovalStepActions.Actions.AIRLOCK_2FA_PASSCODE_REQUIRED;
      }

      enum Actions implements StepAction {
         AIRLOCK_2FA_OTP_REQUIRED,
         AIRLOCK_2FA_POLLING_OR_OTP_REQUIRED;
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class Airlock2FATransactionApprovalStepConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig skipCondition;
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig preCondition;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> approvalFactors;
      @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 ArrayList<InteractiveGotoTargetConfig> interactiveGotoTargets;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<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 Airlock2FASettings airlock2faSettings;
      @SuppressFBWarnings(justification = "generated code")
      private String mobileOnlyRedirectUri;
      @SuppressFBWarnings(justification = "generated code")
      private Airlock2FAMessageProviderConfig messageProvider;
      @SuppressFBWarnings(justification = "generated code")
      private boolean enablePushToAll;
      @SuppressFBWarnings(justification = "generated code")
      private boolean respectCooldownPeriod;
      @SuppressFBWarnings(justification = "generated code")
      private boolean enableShortLivedOnlineQrCodes;
      @SuppressFBWarnings(justification = "generated code")
      private int shortLivedQrCodeValidity;
      @SuppressFBWarnings(justification = "generated code")
      private int shortLivedQrCodeValidityOverlap;
      @SuppressFBWarnings(justification = "generated code")
      private int shortLivedSessionTimeout;

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder approvalFactor(String approvalFactor) {
         if (this.approvalFactors == null) {
            this.approvalFactors = new ArrayList<>();
         }

         this.approvalFactors.add(approvalFactor);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder approvalFactors(
         Collection<? extends String> approvalFactors
      ) {
         if (approvalFactors == null) {
            throw new NullPointerException("approvalFactors cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder clearApprovalFactors() {
         if (this.approvalFactors != null) {
            this.approvalFactors.clear();
         }

         return this;
      }

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

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder 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 Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder clearInteractiveGotoTargets() {
         if (this.interactiveGotoTargets != null) {
            this.interactiveGotoTargets.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder dynamicStepActivation(
         DynamicStepActivationConfig dynamicStepActivation
      ) {
         if (this.dynamicStepActivations == null) {
            this.dynamicStepActivations = new ArrayList<>();
         }

         this.dynamicStepActivations.add(dynamicStepActivation);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder dynamicStepActivations(
         Collection<? extends DynamicStepActivationConfig> dynamicStepActivations
      ) {
         if (dynamicStepActivations == null) {
            throw new NullPointerException("dynamicStepActivations cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder clearDynamicStepActivations() {
         if (this.dynamicStepActivations != null) {
            this.dynamicStepActivations.clear();
         }

         return this;
      }

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder airlock2faSettings(Airlock2FASettings airlock2faSettings) {
         this.airlock2faSettings = airlock2faSettings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder mobileOnlyRedirectUri(
         @Nullable String mobileOnlyRedirectUri
      ) {
         this.mobileOnlyRedirectUri = mobileOnlyRedirectUri;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder messageProvider(
         Airlock2FAMessageProviderConfig messageProvider
      ) {
         this.messageProvider = messageProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder enablePushToAll(boolean enablePushToAll) {
         this.enablePushToAll = enablePushToAll;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder respectCooldownPeriod(boolean respectCooldownPeriod) {
         this.respectCooldownPeriod = respectCooldownPeriod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder enableShortLivedOnlineQrCodes(
         boolean enableShortLivedOnlineQrCodes
      ) {
         this.enableShortLivedOnlineQrCodes = enableShortLivedOnlineQrCodes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder shortLivedQrCodeValidity(int shortLivedQrCodeValidity) {
         this.shortLivedQrCodeValidity = shortLivedQrCodeValidity;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder shortLivedQrCodeValidityOverlap(
         int shortLivedQrCodeValidityOverlap
      ) {
         this.shortLivedQrCodeValidityOverlap = shortLivedQrCodeValidityOverlap;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder shortLivedSessionTimeout(int shortLivedSessionTimeout) {
         this.shortLivedSessionTimeout = shortLivedSessionTimeout;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FATransactionApprovalStepConfig build() {
         List approvalFactors = switch (this.approvalFactors == null ? 0 : this.approvalFactors.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.approvalFactors.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.approvalFactors));
         };

         List interactiveGotoTargets = 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));
         };

         return new Airlock2FATransactionApprovalStepConfig(
            this.skipCondition,
            this.preCondition,
            approvalFactors,
            this.requiresActivation,
            this.tagsOnSuccess,
            this.stepId,
            interactiveGotoTargets,
            switch (this.dynamicStepActivations == null ? 0 : this.dynamicStepActivations.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.dynamicStepActivations.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.dynamicStepActivations));
            },
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.airlock2faSettings,
            this.mobileOnlyRedirectUri,
            this.messageProvider,
            this.enablePushToAll,
            this.respectCooldownPeriod,
            this.enableShortLivedOnlineQrCodes,
            this.shortLivedQrCodeValidity,
            this.shortLivedQrCodeValidityOverlap,
            this.shortLivedSessionTimeout
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", approvalFactors="
            + this.approvalFactors
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", interactiveGotoTargets="
            + this.interactiveGotoTargets
            + ", dynamicStepActivations="
            + this.dynamicStepActivations
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", airlock2faSettings="
            + this.airlock2faSettings
            + ", mobileOnlyRedirectUri="
            + this.mobileOnlyRedirectUri
            + ", messageProvider="
            + this.messageProvider
            + ", enablePushToAll="
            + this.enablePushToAll
            + ", respectCooldownPeriod="
            + this.respectCooldownPeriod
            + ", enableShortLivedOnlineQrCodes="
            + this.enableShortLivedOnlineQrCodes
            + ", shortLivedQrCodeValidity="
            + this.shortLivedQrCodeValidity
            + ", shortLivedQrCodeValidityOverlap="
            + this.shortLivedQrCodeValidityOverlap
            + ", shortLivedSessionTimeout="
            + this.shortLivedSessionTimeout
            + ")";
      }
   }

   public static final class Airlock2FATransactionApprovalStepErrors implements Airlock2FAApprovalStepErrors {
      public StepError qrCodeOtpWrong() {
         return Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepErrors.Errors.AIRLOCK_2FA_OTP_WRONG;
      }

      public StepError deviceChoiceFailed() {
         return com.airlock.iam.flow.shared.application.configuration.airlock2fa.DefaultAirlock2FAApprovalStepErrors.Errors.AIRLOCK_2FA_DEVICE_CHOICE_FAILED;
      }

      public StepError approvalFailed() {
         return Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepErrors.Errors.TRANSACTION_DENIED;
      }

      public StepError passcodeWrong() {
         return Airlock2FATransactionApprovalStepConfig.Airlock2FATransactionApprovalStepErrors.Errors.AIRLOCK_2FA_PASSCODE_WRONG;
      }

      private enum Errors implements StepError {
         AIRLOCK_2FA_OTP_WRONG,
         TRANSACTION_DENIED,
         AIRLOCK_2FA_PASSCODE_WRONG;
      }
   }
}
