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

import com.airlock.iam.core.misc.authen.UsernameTransformer;
import com.airlock.iam.core.misc.impl.authen.RadiusAuthenticator;
import com.airlock.iam.core.misc.plugin.config.validation.CharsetValidator;
import com.airlock.iam.core.misc.util.radius.RadiusConnectionSettings;
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.domain.model.otp.GenericOtpCheckingStep;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForAuthenticationFlows;
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.property.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.plugin.api.annotation.property.TestletProperty;
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;

@Plugin(
   description = "<p>Flow step to check an OTP by calling a RADIUS server (e.g. SecurID).</p><p>Note that the first request sent to the RADIUS server already contains the token code. It is therefore limited to RADIUS servers accepting the OTP with the first request. OTP authentication schemes that first send the OTP to the user (SMS, Email) are not supported.</p><p>The plugin usually works with the default configuration for basic use-cases. To interpret vendor-specific features like \"new PIN required\", corresponding rules have to be configured. </p>",
   displayName = "OTP Check via RADIUS Step"
)
@Groups(
   groupSequence = {"Basic Settings", "Response Mapping Rules", "Tags/Guards", "Flow Control", "Advanced Settings"},
   openGroups = {"Basic Settings", "Response Mapping Rules"}
)
@NextActions(GenericOtpCheckingStep.GenericOtpStepActions.class)
public class RadiusOtpAuthStepConfig
   extends AbstractInteractiveFlowStepConfig
   implements StepConfigForAuthenticationFlows,
   StepConfigForTransactionApprovalFlows {
   @PluginListProperty(
      description = "The RADIUS server(s) to connect to. If more than one is provided, the list is used for failover.",
      optional = false,
      minElements = 1
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   @TestletProperty(buttonToolTip = "Test connection to Radius server", testletClass = RadiusAuthenticator.RadiusAuthenticationTestlet.class)
   private List<RadiusConnectionSettings> radiusServers;
   @BooleanProperty(
      description = "If enabled, the RADIUS attributes sent to the server and received from the server are logged at info level.<br/>This is useful during integration and for debugging but it is generally not suitable for productive systems.",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean logRadiusAttributes;
   @PluginListProperty(
      description = "<p>Defines a list of rules (processed in order of definition) that define how to map RADIUS access reject response's reply messages to authentication results.</p>If no rules are defined or no rule matches, an unspecified authentication failure is used for access reject responses.",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Response Mapping Rules")
   private List<OtpCheckAccessRejectRule> accessRejectRules;
   @PluginListProperty(
      description = "<p>Defines a list of rules (processed in order of definition) that define how to map RADIUS access challenge response's reply messages to authentication results.</p>If no rules are defined or no rule matches, an unspecified authentication failure is used for access challenge responses.",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Response Mapping Rules")
   private List<OtpCheckAccessChallengeRule> accessChallengeRules;
   @StringProperty(
      description = "Defines how the RADIUS authentication process is reported in the log (used for auditing, information and statistics): It can be desirable to report the actual authentication process type used on the RADIUS server side.",
      defaultValue = "RADIUS",
      maxLength = 23,
      allowedValues = {"RADIUS", "SECURID", "HW_OTP", "SW_OTP"},
      optional = true
   )
   @GroupProperty(groupIdentifier = "Response Mapping Rules")
   private String reportedAuthMethod;
   @StringProperty(
      description = "<p>The Network access server identifier (NAS Identifier) to set in all requests.</p>\n<p>The NAS Identifier is used to notify the source of a RADIUS access request, enabling the RADIUS server to\nchoose a policy for that request.\nIf set, the RADIUS server does not have to rely on a set of, potentially changing, IP addresses\n(for example when deploying multiple Airlock IAM servers).\nAlso, different flows may require different policies.</p>\n",
      optional = true,
      minLength = 3,
      displayName = "NAS Identifier"
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String nasIdentifier;
   @PluginListProperty(
      displayName = "Username Transformation",
      description = "<p>Transforms the user ID to the username that is sent to the RADIUS server.</p><p>Note that not all Username Transformer plugins are useful for this purpose since here they are not used to find the user ID, but rather a user alias.</p>",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private List<UsernameTransformer> usernameTransformers;
   @StringProperty(
      description = "The encoding for the RADIUS attributes in an authentication request. The encoding should be the same as used on the RADIUS server.",
      optional = true,
      defaultValue = "UTF-8",
      suggestedValues = {"UTF-8", "ISO-8859-1", "ISO-8859-15"}
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String encoding;
   private static final String ENCODING_FIELD = "encoding";

   @ConfigurationValidator
   public static void validateConfiguration(
      PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment
   ) throws ConfigurationException {
      CharsetValidator.validateCharsetProperty("encoding", config, env, context);
   }

   protected RadiusOtpAuthStepConfig(
      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,
      List<RadiusConnectionSettings> radiusServers,
      boolean logRadiusAttributes,
      List<OtpCheckAccessRejectRule> accessRejectRules,
      List<OtpCheckAccessChallengeRule> accessChallengeRules,
      String reportedAuthMethod,
      String nasIdentifier,
      List<UsernameTransformer> usernameTransformers,
      String encoding
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.radiusServers = radiusServers;
      this.logRadiusAttributes = logRadiusAttributes;
      this.accessRejectRules = accessRejectRules;
      this.accessChallengeRules = accessChallengeRules;
      this.reportedAuthMethod = reportedAuthMethod;
      this.nasIdentifier = nasIdentifier;
      this.usernameTransformers = usernameTransformers;
      this.encoding = encoding;
   }

   public List<RadiusConnectionSettings> getRadiusServers() {
      return this.radiusServers;
   }

   public boolean isLogRadiusAttributes() {
      return this.logRadiusAttributes;
   }

   public List<OtpCheckAccessRejectRule> getAccessRejectRules() {
      return this.accessRejectRules;
   }

   public List<OtpCheckAccessChallengeRule> getAccessChallengeRules() {
      return this.accessChallengeRules;
   }

   public String getReportedAuthMethod() {
      return this.reportedAuthMethod;
   }

   public String getNasIdentifier() {
      return this.nasIdentifier;
   }

   public List<UsernameTransformer> getUsernameTransformers() {
      return this.usernameTransformers;
   }

   public String getEncoding() {
      return this.encoding;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class RadiusOtpAuthStepConfigBuilder {
      @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 ArrayList<RadiusConnectionSettings> radiusServers;
      @SuppressFBWarnings(justification = "generated code")
      private boolean logRadiusAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<OtpCheckAccessRejectRule> accessRejectRules;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<OtpCheckAccessChallengeRule> accessChallengeRules;
      @SuppressFBWarnings(justification = "generated code")
      private String reportedAuthMethod;
      @SuppressFBWarnings(justification = "generated code")
      private String nasIdentifier;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<UsernameTransformer> usernameTransformers;
      @SuppressFBWarnings(justification = "generated code")
      private String encoding;

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

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

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

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

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

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

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

         return this;
      }

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

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

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

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

         return this;
      }

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder radiusServer(RadiusConnectionSettings radiusServer) {
         if (this.radiusServers == null) {
            this.radiusServers = new ArrayList<>();
         }

         this.radiusServers.add(radiusServer);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder radiusServers(Collection<? extends RadiusConnectionSettings> radiusServers) {
         if (radiusServers == null) {
            throw new NullPointerException("radiusServers cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder clearRadiusServers() {
         if (this.radiusServers != null) {
            this.radiusServers.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder logRadiusAttributes(boolean logRadiusAttributes) {
         this.logRadiusAttributes = logRadiusAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder accessRejectRule(OtpCheckAccessRejectRule accessRejectRule) {
         if (this.accessRejectRules == null) {
            this.accessRejectRules = new ArrayList<>();
         }

         this.accessRejectRules.add(accessRejectRule);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder accessRejectRules(Collection<? extends OtpCheckAccessRejectRule> accessRejectRules) {
         if (accessRejectRules == null) {
            throw new NullPointerException("accessRejectRules cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder clearAccessRejectRules() {
         if (this.accessRejectRules != null) {
            this.accessRejectRules.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder accessChallengeRule(OtpCheckAccessChallengeRule accessChallengeRule) {
         if (this.accessChallengeRules == null) {
            this.accessChallengeRules = new ArrayList<>();
         }

         this.accessChallengeRules.add(accessChallengeRule);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder accessChallengeRules(Collection<? extends OtpCheckAccessChallengeRule> accessChallengeRules) {
         if (accessChallengeRules == null) {
            throw new NullPointerException("accessChallengeRules cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder clearAccessChallengeRules() {
         if (this.accessChallengeRules != null) {
            this.accessChallengeRules.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder reportedAuthMethod(String reportedAuthMethod) {
         this.reportedAuthMethod = reportedAuthMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder nasIdentifier(String nasIdentifier) {
         this.nasIdentifier = nasIdentifier;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder usernameTransformer(UsernameTransformer usernameTransformer) {
         if (this.usernameTransformers == null) {
            this.usernameTransformers = new ArrayList<>();
         }

         this.usernameTransformers.add(usernameTransformer);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder usernameTransformers(Collection<? extends UsernameTransformer> usernameTransformers) {
         if (usernameTransformers == null) {
            throw new NullPointerException("usernameTransformers cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder clearUsernameTransformers() {
         if (this.usernameTransformers != null) {
            this.usernameTransformers.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder encoding(String encoding) {
         this.encoding = encoding;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RadiusOtpAuthStepConfig 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));
         };

         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));
         };

         List radiusServers = switch (this.radiusServers == null ? 0 : this.radiusServers.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.radiusServers.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.radiusServers));
         };

         List accessRejectRules = switch (this.accessRejectRules == null ? 0 : this.accessRejectRules.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.accessRejectRules.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.accessRejectRules));
         };

         List accessChallengeRules = switch (this.accessChallengeRules == null ? 0 : this.accessChallengeRules.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.accessChallengeRules.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.accessChallengeRules));
         };

         return new RadiusOtpAuthStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            tagsOnSuccess,
            this.stepId,
            interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            radiusServers,
            this.logRadiusAttributes,
            accessRejectRules,
            accessChallengeRules,
            this.reportedAuthMethod,
            this.nasIdentifier,
            switch (this.usernameTransformers == null ? 0 : this.usernameTransformers.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.usernameTransformers.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.usernameTransformers));
            },
            this.encoding
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "RadiusOtpAuthStepConfig.RadiusOtpAuthStepConfigBuilder(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
            + ", radiusServers="
            + this.radiusServers
            + ", logRadiusAttributes="
            + this.logRadiusAttributes
            + ", accessRejectRules="
            + this.accessRejectRules
            + ", accessChallengeRules="
            + this.accessChallengeRules
            + ", reportedAuthMethod="
            + this.reportedAuthMethod
            + ", nasIdentifier="
            + this.nasIdentifier
            + ", usernameTransformers="
            + this.usernameTransformers
            + ", encoding="
            + this.encoding
            + ")";
      }
   }
}
