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

import com.airlock.iam.authentication.api.domain.model.step.UserIdentifyingStep;
import com.airlock.iam.flow.api.application.configuration.dynamicactivation.DynamicStepActivationConfig;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.step.AbstractInteractiveFlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.InteractiveGotoTargetConfig;
import com.airlock.iam.flow.api.application.configuration.step.StepIdConfig;
import com.airlock.iam.flow.api.application.configuration.tag.TagConfig;
import com.airlock.iam.flow.api.application.configuration.valueprovider.ValueMapProviderConfig;
import com.airlock.iam.flow.shared.api.application.configuration.captcha.CaptchaProviderConfig;
import com.airlock.iam.flow.shared.api.application.configuration.item.SelfServiceItemDefinitionConfig;
import com.airlock.iam.flow.shared.application.configuration.captcha.CaptchaStepConfig;
import com.airlock.iam.flow.shared.application.configuration.item.StringContextDataItemDefinitionConfig;
import com.airlock.iam.flow.shared.application.configuration.userdata.CommonUserDataStepConfig;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForAuthenticationFlows;
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.PluginProperty;
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>This step identifies users by their context data items (user data).</p>\n<p><b>Caution:</b> This step allows user enumeration attacks. Such attacks cannot be prevented,\neven if \"Prevent User-Enumeration\" is enabled on the \"Authentication Flow\".</p>",
   displayName = "User Identification By Data Step"
)
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control"})
@NextActions(UserIdentifyingStep.UserIdentifyingStepAction.class)
public final class UserByDataIdentifyingStepConfig
   extends AbstractInteractiveFlowStepConfig
   implements StepConfigForAuthenticationFlows,
   CaptchaStepConfig,
   CommonUserDataStepConfig {
   @PluginListProperty(
      description = "<p>The context data items that are used to identify a user. Combined, they must uniquely identify a user.</p>\n<p>Each item defines whether it is required for identification or not.</p>\n<p>Required items must be provided for identification and must successfully validate (see validators on each item configuration).</p>\n<p>Optional items can be omitted, but if they are provided, they must also successfully validate for the step to succeed.</p>",
      optional = false,
      minElements = 1
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private List<StringContextDataItemDefinitionConfig> contextDataItems;
   @PluginProperty(
      description = "If a CAPTCHA is configured, a CAPTCHA challenge will be added to: <ul><li>the response of the flow selecting request (when this step is the first interactive step in a flow). </li><li>the step response immediately preceding the protected step (when this step is not the first interactive step in a flow). </li></ul>To complete this step and proceed with the flow, the CAPTCHA must be solved successfully.",
      optional = true,
      displayName = "CAPTCHA"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private CaptchaProviderConfig captchaProvider;
   @BooleanProperty(
      description = "If this option is enabled, the strings entered to identify a user are compared without taking upper/lower case into account.\n<p>This is an improvement in user-friendliness, but carries a higher risk that the user cannot be clearly identified, resulting in a step error.</p>",
      optional = true,
      defaultValue = "false",
      displayName = "Ignore Upper/Lower Case"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean ignoreCase;

   private UserByDataIdentifyingStepConfig(
      CaptchaProviderConfig captchaProvider,
      List<StringContextDataItemDefinitionConfig> contextDataItems,
      boolean ignoreCase,
      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
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.captchaProvider = captchaProvider;
      this.contextDataItems = contextDataItems;
      this.ignoreCase = ignoreCase;
   }

   @Override
   public List<SelfServiceItemDefinitionConfig> getUserDataItems() {
      return this.contextDataItems.stream().map(p -> (SelfServiceItemDefinitionConfig)p).toList();
   }

   public List<StringContextDataItemDefinitionConfig> getContextDataItems() {
      return this.contextDataItems;
   }

   @Override
   public CaptchaProviderConfig getCaptchaProvider() {
      return this.captchaProvider;
   }

   public boolean isIgnoreCase() {
      return this.ignoreCase;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class UserByDataIdentifyingStepConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private CaptchaProviderConfig captchaProvider;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<StringContextDataItemDefinitionConfig> contextDataItems;
      @SuppressFBWarnings(justification = "generated code")
      private boolean ignoreCase;
      @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 ArrayList<DynamicStepActivationConfig> dynamicStepActivations;
      @SuppressFBWarnings(justification = "generated code")
      private Map<String, StepIdConfig> onFailureGotos;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<ValueMapProviderConfig> customResponseAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<ValueMapProviderConfig> customFailureResponseAttributes;

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

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder captchaProvider(CaptchaProviderConfig captchaProvider) {
         this.captchaProvider = captchaProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder contextDataItem(StringContextDataItemDefinitionConfig contextDataItem) {
         if (this.contextDataItems == null) {
            this.contextDataItems = new ArrayList<>();
         }

         this.contextDataItems.add(contextDataItem);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder contextDataItems(
         Collection<? extends StringContextDataItemDefinitionConfig> contextDataItems
      ) {
         if (contextDataItems == null) {
            throw new NullPointerException("contextDataItems cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder clearContextDataItems() {
         if (this.contextDataItems != null) {
            this.contextDataItems.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder ignoreCase(boolean ignoreCase) {
         this.ignoreCase = ignoreCase;
         return this;
      }

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

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

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

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

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

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

         return this;
      }

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

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

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

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

         return this;
      }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder 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 UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder clearDynamicStepActivations() {
         if (this.dynamicStepActivations != null) {
            this.dynamicStepActivations.clear();
         }

         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder customResponseAttribute(ValueMapProviderConfig customResponseAttribute) {
         if (this.customResponseAttributes == null) {
            this.customResponseAttributes = new ArrayList<>();
         }

         this.customResponseAttributes.add(customResponseAttribute);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder customResponseAttributes(
         Collection<? extends ValueMapProviderConfig> customResponseAttributes
      ) {
         if (customResponseAttributes == null) {
            throw new NullPointerException("customResponseAttributes cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder clearCustomResponseAttributes() {
         if (this.customResponseAttributes != null) {
            this.customResponseAttributes.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder customFailureResponseAttribute(
         ValueMapProviderConfig customFailureResponseAttribute
      ) {
         if (this.customFailureResponseAttributes == null) {
            this.customFailureResponseAttributes = new ArrayList<>();
         }

         this.customFailureResponseAttributes.add(customFailureResponseAttribute);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder customFailureResponseAttributes(
         Collection<? extends ValueMapProviderConfig> customFailureResponseAttributes
      ) {
         if (customFailureResponseAttributes == null) {
            throw new NullPointerException("customFailureResponseAttributes cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder clearCustomFailureResponseAttributes() {
         if (this.customFailureResponseAttributes != null) {
            this.customFailureResponseAttributes.clear();
         }

         return this;
      }

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

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

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

         return new UserByDataIdentifyingStepConfig(
            this.captchaProvider,
            contextDataItems,
            this.ignoreCase,
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            tagsOnSuccess,
            this.stepId,
            interactiveGotoTargets,
            dynamicStepActivations,
            this.onFailureGotos,
            customResponseAttributes,
            switch (this.customFailureResponseAttributes == null ? 0 : this.customFailureResponseAttributes.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.customFailureResponseAttributes.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.customFailureResponseAttributes));
            }
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "UserByDataIdentifyingStepConfig.UserByDataIdentifyingStepConfigBuilder(captchaProvider="
            + this.captchaProvider
            + ", contextDataItems="
            + this.contextDataItems
            + ", ignoreCase="
            + this.ignoreCase
            + ", 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
            + ")";
      }
   }
}
