package com.airlock.iam.authentication.application.configuration.migration;

import com.airlock.iam.authentication.application.configuration.migration.condition.NeverMigrateConditionConfig;
import com.airlock.iam.authentication.application.configuration.migration.condition.SkipMigrationConditionConfig;
import com.airlock.iam.authentication.domain.model.migration.MigrationSelectionStep;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.flow.api.application.configuration.dynamicactivation.DynamicStepActivationConfig;
import com.airlock.iam.flow.api.application.configuration.selection.CommonSelectionOptionConfig;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.selection.SelectionFlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.sequence.FlowStepSequence;
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.api.domain.model.selection.Option;
import com.airlock.iam.flow.application.configuration.selection.step.InitializedSelectionOptions;
import com.airlock.iam.flow.shared.application.configuration.step.NoOperationStepConfig;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForAuthenticationFlows;
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.lifecycle.AfterConfigActivation;
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.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
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 com.airlock.iam.plugin.api.exception.PropertyConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.exception.PropertyTypeMismatchException;
import com.airlock.iam.plugin.api.valuereader.BooleanPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.PluginListPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.PluginPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import com.airlock.iam.plugin.framework.application.service.builder.plugin.PluginPropertyBuilderInitializer;
import com.airlock.iam.plugin.framework.domain.service.validation.DurationParser;
import com.airlock.iam.plugin.framework.domain.service.validation.DurationValidator;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Plugin(description = "Selection between different migration subflows depending on configurable options.", displayName = "Migration Selection Step")
@Groups(openGroups = "Basic Settings")
@NextActions(MigrationSelectionStep.MigrationStepActions.class)
public final class MigrationSelectionStepConfig extends AbstractInteractiveFlowStepConfig implements SelectionFlowStepConfig, StepConfigForAuthenticationFlows {
   public static final String MIGRATION_SELECTION_STEP_DISPLAY_NAME = "Migration Selection Step";
   private static final String NEVER_MIGRATE_SETTINGS = "Never Migrate Settings";
   private static final String SKIP_MIGRATION_SETTINGS = "Skip Migration Settings";
   public static final String NEVER_MIGRATE = "NEVER_MIGRATE";
   public static final String SKIP_MIGRATION = "SKIP_MIGRATION";
   private static final List<StepConfigForAuthenticationFlows> NEVER_MIGRATE_STEPS = List.of(
      ((NeverMigrateStepConfig.NeverMigrateStepConfigBuilder)PluginPropertyBuilderInitializer.builder(
            NeverMigrateStepConfig.NeverMigrateStepConfigBuilder.class
         ))
         .build()
   );
   private static final List<StepConfigForAuthenticationFlows> NO_OPERATION_STEPS = List.of(
      ((NoOperationStepConfig.NoOperationStepConfigBuilder)PluginPropertyBuilderInitializer.builder(NoOperationStepConfig.NoOperationStepConfigBuilder.class))
         .build()
   );
   private FlowConditionConfig initializedDefaultFlowCondition;
   @PluginListProperty(
      description = "All available options for this migration selection. For each option, a condition can be configured that determines if it is available in a specific situation and for a particular user. If no option is available the step fails.",
      optional = false,
      minElements = 1,
      displayName = "Available options"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private List<MigrationSelectionOptionConfig> availableOptions;
   @BooleanProperty(
      description = "Activate the feature \"Never Migrate\" allowing the user to permanently reject the migration.<p>If this property is enabled, it is possible for a user to reject the migration if:<ul><li>No migration date is set for the user <i>OR</i></li><li>A migration date is set and the property \"Allow 'Never Migrate' Despite Migration Date\" is enabled.</li></ul></p>",
      optional = true,
      defaultValue = "false",
      displayName = "Never Migrate Possible"
   )
   @GroupProperty(groupIdentifier = "Never Migrate Settings")
   private boolean neverMigratePossible;
   public static final String NEVER_MIGRATE_POSSIBLE_PROPERTY = "neverMigratePossible";
   private static final String NEVER_MIGRATE_POSSIBLE_DISPLAY_NAME = "Never Migrate Possible";
   @BooleanProperty(
      description = "Enables the feature \"Never Migrate\" even if there is a migration date set for the corresponding user.<p>Whether it is possible to \"Never Migrate\" depends on whether or not a migration date is set and whether the authentication is happening during the \"Never Migrate Period\" (if set at all).</p>",
      optional = true,
      defaultValue = "false",
      displayName = "Never Migrate Possible Despite Migration Date"
   )
   @GroupProperty(groupIdentifier = "Never Migrate Settings")
   private boolean neverMigratePossibleWithToDate;
   public static final String NEVER_MIGRATE_POSSIBLE_WITH_TO_DATE_PROPERTY = "neverMigratePossibleWithToDate";
   public static final String NEVER_MIGRATE_POSSIBLE_WITH_TO_DATE_DISPLAY_NAME = "Never Migrate Possible Despite Migration Date";
   @StringProperty(
      description = "<p>Defines the period before the migration date in which the feature \"Never Migrate\" is active.</p>Prerequisites for this property to take effect:<ul><li>A migration date must be set for the user.</li><li>\"Never Migrate Possible\" must be enabled.</li><li>\"Never Migrate Possible Despite Migration Date\" must be enabled.</li><li>At least one available option must define a \"Hint Period\" greater or equal to this property.</li></ul><p>If this property is not set, the feature \"Never Migrate\" is possible (if enabled) during and after the entire \"Hint Period\".</p><p>The duration must be specified in the format \"(d)ays (h)ours (m)inutes (s)econds\" e.g. \"2d 4h 10m 5s\" (any part can be omitted).</p>",
      optional = true,
      exampleValues = {"7d", "30d", "14d 6h 30m"},
      displayName = "Never Migrate Period"
   )
   @GroupProperty(groupIdentifier = "Never Migrate Settings")
   private String neverMigratePeriod;
   public static final String NEVER_MIGRATE_PERIOD_PROPERTY = "neverMigratePeriod";
   private Optional<Duration> neverMigratePeriodDuration;
   @PluginProperty(
      description = "By default the \"Never Migrate\" condition is considered to decide whether it is possible for a user to reject the migration. This default condition can be overwritten by configuring a custom condition.",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Never Migrate Settings")
   private FlowConditionConfig customNeverMigrateCondition;
   public static final String CUSTOM_MIGRATION_CONDITON_PROPERTY = "customNeverMigrateCondition";
   @PluginListProperty(
      description = "By default the \"Never Migrate\" step is executed if the \"Never Migrate\" condition is fulfilled. This default step can be overwritten by configuring a custom step.",
      minElements = 1,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Never Migrate Settings")
   private List<StepConfigForAuthenticationFlows> customNeverMigrateSteps;
   public static final String CUSTOM_NEVER_MIGRATE_STEPS_PROPERTY = "customNeverMigrateSteps";
   @PluginListProperty(
      description = "By default the \"No Operation Step\" step is executed if the \"Skip Migration\" condition is fulfilled. This default step can be overwritten by configuring a custom step.",
      minElements = 1,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Skip Migration Settings")
   private List<StepConfigForAuthenticationFlows> customSkipMigrationSteps;
   public static final String CUSTOM_SKIP_MIGRATION_STEPS_PROPERTY = "customSkipMigrationSteps";
   private final List<TagConfig> tagsOnSuccess = Collections.emptyList();
   private List<MigrationSelectionOptionConfig> allMigrationOptions;
   private InitializedSelectionOptions initializedOptions;

   @Override
   public List<TagConfig> getTagsOnSuccess() {
      return Collections.emptyList();
   }

   @Override
   public Set<Option> getOptions() {
      return this.initializedOptions.getOptionSet();
   }

   @Override
   public FlowStepSequence getSubFlowForOption(Option option) {
      return this.initializedOptions.getSubFlowForOption(option);
   }

   @ConfigurationValidator
   static void validateOptionNamesUnique(PluginConfiguration pc, ConfigurationEnvironment env, ConfigurationContext context, Environment valEnv) throws PropertyConfigurationException {
      List<PluginConfiguration> allOptions = PluginListPropertyValueReader.effectivePluginListPropertyValue(pc, "availableOptions", env, context);
      Set<String> seenNames = new HashSet<>();

      for (PluginConfiguration optionConfig : allOptions) {
         String name = extractOptionName(optionConfig, env, context);
         if (name != null && !seenNames.add(name)) {
            throw new PropertyConstraintViolationException(
               "availableOptions",
               pc,
               env,
               context,
               "Option name (or \"Target Auth Method\" for Default Migration Selection Options) " + LogUtil.quote(name) + " is not unique"
            );
         }
      }
   }

   private static String extractOptionName(PluginConfiguration optionConfig, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyTypeMismatchException {
      if (SimpleMigrationSelectionOptionConfig.class.getName().equals(optionConfig.getPluginClassName())) {
         return StringPropertyValueReader.effectiveStringConfigValue(optionConfig, "targetAuthMethod", env, context);
      } else {
         return AdvancedMigrationSelectionOptionConfig.class.getName().equals(optionConfig.getPluginClassName())
            ? StringPropertyValueReader.effectiveStringConfigValue(optionConfig, "optionName", env, context)
            : null;
      }
   }

   @ConfigurationValidator
   public static void validateNeverMigrationPeriodDuration(
      PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment valEnv
   ) throws ConfigurationException {
      DurationValidator.validateDurationFieldIfSet(config, "neverMigratePeriod", env, context);
   }

   @ConfigurationValidator
   static void validateNeverMigrateSettings(PluginConfiguration pc, ConfigurationEnvironment env, ConfigurationContext context, Environment valEnv) throws PropertyTypeMismatchException, PropertyConstraintViolationException {
      boolean isNeverMigratePossible = BooleanPropertyValueReader.effectiveBooleanPropertyValue(pc, "neverMigratePossible", env, context);
      boolean isNeverMigratePossibleWithToDate = BooleanPropertyValueReader.effectiveBooleanPropertyValue(pc, "neverMigratePossibleWithToDate", env, context);
      if (!isNeverMigratePossible && isNeverMigratePossibleWithToDate) {
         throw new PropertyConstraintViolationException(
            "neverMigratePossible",
            pc,
            env,
            context,
            LogUtil.quote("Never Migrate Possible") + " must be enabled to configure " + LogUtil.quote("Never Migrate Possible Despite Migration Date")
         );
      }

      String neverMigratePeriod = StringPropertyValueReader.effectiveStringConfigValue(pc, "neverMigratePeriod", env, context);
      if (!isNeverMigratePossible && neverMigratePeriod != null) {
         throw new PropertyConstraintViolationException(
            "neverMigratePossible", pc, env, context, LogUtil.quote("Never Migrate Possible") + " must be enabled to configure the \"Never Migrate Period\"."
         );
      }

      if (!isNeverMigratePossibleWithToDate && neverMigratePeriod != null) {
         throw new PropertyConstraintViolationException(
            "neverMigratePossibleWithToDate",
            pc,
            env,
            context,
            LogUtil.quote("Never Migrate Possible Despite Migration Date") + " must be enabled to configure the \"Never Migrate Period\"."
         );
      }

      PluginConfiguration customMigrationCondition = PluginPropertyValueReader.effectivePluginPropertyValue(pc, "customNeverMigrateCondition", env, context);
      if (!isNeverMigratePossible && customMigrationCondition != null) {
         throw new PropertyConstraintViolationException(
            "neverMigratePossible",
            pc,
            env,
            context,
            LogUtil.quote("Never Migrate Possible") + " must be enabled to configure a custom never migrate condition."
         );
      }

      List<PluginConfiguration> customMigrationSteps = PluginListPropertyValueReader.effectivePluginListPropertyValue(
         pc, "customNeverMigrateSteps", env, context
      );
      if (!isNeverMigratePossible && !customMigrationSteps.isEmpty()) {
         throw new PropertyConstraintViolationException(
            "neverMigratePossible", pc, env, context, LogUtil.quote("Never Migrate Possible") + " must be enabled to configure custom never migrate steps."
         );
      }
   }

   @AfterConfigActivation
   void initializeOptions() {
      this.parseNeverMigratePeriodDuration();
      this.initializeDefaultFlowConditionConfig();
      this.allMigrationOptions = new ArrayList<>(this.availableOptions);
      if (this.neverMigratePossible) {
         this.allMigrationOptions.add(this.createNeverMigrateOption());
      }

      this.allMigrationOptions.add(this.createSkipMigrationOption());
      Map<Option, FlowStepSequence> optionsMap = this.allMigrationOptions
         .stream()
         .collect(Collectors.toMap(CommonSelectionOptionConfig::getOption, CommonSelectionOptionConfig::getFlowStepSequence));
      this.initializedOptions = new InitializedSelectionOptions(optionsMap);
   }

   private void parseNeverMigratePeriodDuration() {
      this.neverMigratePeriodDuration = Optional.ofNullable(this.neverMigratePeriod).map(d -> new DurationParser(d).getDuration());
   }

   private void initializeDefaultFlowConditionConfig() {
      this.initializedDefaultFlowCondition = ((NeverMigrateConditionConfig.NeverMigrateConditionConfigBuilder)PluginPropertyBuilderInitializer.builder(
            NeverMigrateConditionConfig.NeverMigrateConditionConfigBuilder.class
         ))
         .neverMigratePossibleWithToDate(this.neverMigratePossibleWithToDate)
         .neverMigratePeriod(this.neverMigratePeriod)
         .build();
   }

   private MigrationSelectionOptionConfig createNeverMigrateOption() {
      return ((AdvancedMigrationSelectionOptionConfig.AdvancedMigrationSelectionOptionConfigBuilder)PluginPropertyBuilderInitializer.builder(
            AdvancedMigrationSelectionOptionConfig.AdvancedMigrationSelectionOptionConfigBuilder.class
         ))
         .optionName("NEVER_MIGRATE")
         .steps(this.createNeverMigrateStep())
         .condition(this.createNeverMigrateCondition())
         .build();
   }

   private FlowConditionConfig createNeverMigrateCondition() {
      return this.customNeverMigrateCondition == null ? this.initializedDefaultFlowCondition : this.customNeverMigrateCondition;
   }

   private MigrationSelectionOptionConfig createSkipMigrationOption() {
      return ((AdvancedMigrationSelectionOptionConfig.AdvancedMigrationSelectionOptionConfigBuilder)PluginPropertyBuilderInitializer.builder(
            AdvancedMigrationSelectionOptionConfig.AdvancedMigrationSelectionOptionConfigBuilder.class
         ))
         .optionName("SKIP_MIGRATION")
         .steps(this.createSkipMigrationSteps())
         .condition(this.createSkipMigrationCondition())
         .build();
   }

   private FlowConditionConfig createSkipMigrationCondition() {
      return new SkipMigrationConditionConfig(this.availableOptions);
   }

   private List<StepConfigForAuthenticationFlows> createNeverMigrateStep() {
      return this.customNeverMigrateSteps.isEmpty() ? NEVER_MIGRATE_STEPS : this.customNeverMigrateSteps;
   }

   private List<StepConfigForAuthenticationFlows> createSkipMigrationSteps() {
      return this.customSkipMigrationSteps.isEmpty() ? NO_OPERATION_STEPS : this.customSkipMigrationSteps;
   }

   MigrationSelectionStepConfig(
      List<MigrationSelectionOptionConfig> availableOptions,
      boolean neverMigratePossible,
      boolean neverMigratePossibleWithToDate,
      String neverMigratePeriod,
      FlowConditionConfig customNeverMigrateCondition,
      List<StepConfigForAuthenticationFlows> customNeverMigrateSteps,
      List<StepConfigForAuthenticationFlows> customSkipMigrationSteps,
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      @Nullable StepIdConfig stepId,
      List<InteractiveGotoTargetConfig> interactiveGotoTargets,
      List<DynamicStepActivationConfig> dynamicStepActivations,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         Collections.emptyList(),
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.availableOptions = availableOptions;
      this.neverMigratePossible = neverMigratePossible;
      this.neverMigratePossibleWithToDate = neverMigratePossibleWithToDate;
      this.neverMigratePeriod = neverMigratePeriod;
      this.customNeverMigrateCondition = customNeverMigrateCondition;
      this.customNeverMigrateSteps = customNeverMigrateSteps;
      this.customSkipMigrationSteps = customSkipMigrationSteps;
   }

   public FlowConditionConfig getInitializedDefaultFlowCondition() {
      return this.initializedDefaultFlowCondition;
   }

   @Override
   public List<MigrationSelectionOptionConfig> getAvailableOptions() {
      return this.availableOptions;
   }

   public boolean isNeverMigratePossible() {
      return this.neverMigratePossible;
   }

   public boolean isNeverMigratePossibleWithToDate() {
      return this.neverMigratePossibleWithToDate;
   }

   public String getNeverMigratePeriod() {
      return this.neverMigratePeriod;
   }

   public Optional<Duration> getNeverMigratePeriodDuration() {
      return this.neverMigratePeriodDuration;
   }

   public FlowConditionConfig getCustomNeverMigrateCondition() {
      return this.customNeverMigrateCondition;
   }

   public List<StepConfigForAuthenticationFlows> getCustomNeverMigrateSteps() {
      return this.customNeverMigrateSteps;
   }

   public List<StepConfigForAuthenticationFlows> getCustomSkipMigrationSteps() {
      return this.customSkipMigrationSteps;
   }

   public List<MigrationSelectionOptionConfig> getAllMigrationOptions() {
      return this.allMigrationOptions;
   }

   public InitializedSelectionOptions getInitializedOptions() {
      return this.initializedOptions;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class MigrationSelectionStepConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<MigrationSelectionOptionConfig> availableOptions;
      @SuppressFBWarnings(justification = "generated code")
      private boolean neverMigratePossible;
      @SuppressFBWarnings(justification = "generated code")
      private boolean neverMigratePossibleWithToDate;
      @SuppressFBWarnings(justification = "generated code")
      private String neverMigratePeriod;
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig customNeverMigrateCondition;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<StepConfigForAuthenticationFlows> customNeverMigrateSteps;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<StepConfigForAuthenticationFlows> customSkipMigrationSteps;
      @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 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")
      MigrationSelectionStepConfigBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder availableOption(MigrationSelectionOptionConfig availableOption) {
         if (this.availableOptions == null) {
            this.availableOptions = new ArrayList<>();
         }

         this.availableOptions.add(availableOption);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder availableOptions(
         Collection<? extends MigrationSelectionOptionConfig> availableOptions
      ) {
         if (availableOptions == null) {
            throw new NullPointerException("availableOptions cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder clearAvailableOptions() {
         if (this.availableOptions != null) {
            this.availableOptions.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder neverMigratePossible(boolean neverMigratePossible) {
         this.neverMigratePossible = neverMigratePossible;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder neverMigratePossibleWithToDate(boolean neverMigratePossibleWithToDate) {
         this.neverMigratePossibleWithToDate = neverMigratePossibleWithToDate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder neverMigratePeriod(String neverMigratePeriod) {
         this.neverMigratePeriod = neverMigratePeriod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder customNeverMigrateCondition(FlowConditionConfig customNeverMigrateCondition) {
         this.customNeverMigrateCondition = customNeverMigrateCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder customNeverMigrateStep(StepConfigForAuthenticationFlows customNeverMigrateStep) {
         if (this.customNeverMigrateSteps == null) {
            this.customNeverMigrateSteps = new ArrayList<>();
         }

         this.customNeverMigrateSteps.add(customNeverMigrateStep);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder customNeverMigrateSteps(
         Collection<? extends StepConfigForAuthenticationFlows> customNeverMigrateSteps
      ) {
         if (customNeverMigrateSteps == null) {
            throw new NullPointerException("customNeverMigrateSteps cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder clearCustomNeverMigrateSteps() {
         if (this.customNeverMigrateSteps != null) {
            this.customNeverMigrateSteps.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder customSkipMigrationStep(StepConfigForAuthenticationFlows customSkipMigrationStep) {
         if (this.customSkipMigrationSteps == null) {
            this.customSkipMigrationSteps = new ArrayList<>();
         }

         this.customSkipMigrationSteps.add(customSkipMigrationStep);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder customSkipMigrationSteps(
         Collection<? extends StepConfigForAuthenticationFlows> customSkipMigrationSteps
      ) {
         if (customSkipMigrationSteps == null) {
            throw new NullPointerException("customSkipMigrationSteps cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder clearCustomSkipMigrationSteps() {
         if (this.customSkipMigrationSteps != null) {
            this.customSkipMigrationSteps.clear();
         }

         return this;
      }

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

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

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

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

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

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

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

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

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

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

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

         return new MigrationSelectionStepConfig(
            availableOptions,
            this.neverMigratePossible,
            this.neverMigratePossibleWithToDate,
            this.neverMigratePeriod,
            this.customNeverMigrateCondition,
            customNeverMigrateSteps,
            switch (this.customSkipMigrationSteps == null ? 0 : this.customSkipMigrationSteps.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.customSkipMigrationSteps.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.customSkipMigrationSteps));
            },
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            this.stepId,
            this.interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "MigrationSelectionStepConfig.MigrationSelectionStepConfigBuilder(availableOptions="
            + this.availableOptions
            + ", neverMigratePossible="
            + this.neverMigratePossible
            + ", neverMigratePossibleWithToDate="
            + this.neverMigratePossibleWithToDate
            + ", neverMigratePeriod="
            + this.neverMigratePeriod
            + ", customNeverMigrateCondition="
            + this.customNeverMigrateCondition
            + ", customNeverMigrateSteps="
            + this.customNeverMigrateSteps
            + ", customSkipMigrationSteps="
            + this.customSkipMigrationSteps
            + ", skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", stepId="
            + this.stepId
            + ", interactiveGotoTargets="
            + this.interactiveGotoTargets
            + ", dynamicStepActivations="
            + this.dynamicStepActivations
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ")";
      }
   }
}
