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

import com.airlock.iam.common.application.configuration.credential.SsoTicketExtractorConfig;
import com.airlock.iam.common.application.configuration.sso.SsoTicketContextDataExtractorConfig;
import com.airlock.iam.common.application.configuration.sso.SsoTicketRoleExtractorConfig;
import com.airlock.iam.core.application.configuration.AcceptedSsoTicketRepositoryConfig;
import com.airlock.iam.core.misc.util.ticket.TicketDecoder;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
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.plugin.api.annotation.config.Groups;
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.StringProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import lombok.NonNull;

@Plugin(
   description = "SSO ticket step that expects an SSO ticket containing at least a user ID (the representee) and a representer ID.",
   displayName = "Representation SSO Ticket Identifying Step"
)
@PluginLicenseRestriction(moduleAffiliation = "Representation")
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control", "Advanced Settings"})
public class RepresentationSsoTicketIdentifyingStepConfig extends SsoTicketIdentifyingStepConfigBase {
   @BooleanProperty(
      description = "<p>If enabled, locked users are allowed to be represented.</p> <p><b>Security note:</b> This disables the regular checks whether a user is locked.</p>",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private boolean allowLockedUsers;
   @StringProperty(
      description = "The identifier of the authentication method for this step. Since the authentication method is also the identifier for failed login attempts for this step, distinct identifiers must be chosen if multiple instances of the same step type are used to check different credentials (e.g., two password step instances check two different passwords).<p>Example: a system tracks two passwords per user. Password 1 is used in flow A, password 2 in flow B. These two steps must have distinct 'Authentication Method Ids', e.g. PASSWORD1 and PASSWORD2.</p>If only one identifier (e.g. \"PASSWORD\") is used, this may allow brute force attacks on the password as follows. Assuming an attacker knows the user's password 1, they get an unlimited number of attempts on flow B, as the 'PASSWORD' counter can repeatedly be reset to 0 by performing a successful login on flow A.<p>To prevent such attacks, use two different counters by setting the authentication methods, for example, to PASSWORD1 and PASSWORD2, respectively.</p>",
      displayName = "Authentication Method ID",
      defaultValue = "SSO_REPRESENT_TICKET",
      maxLength = 23,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String authenticationMethodId;

   public RepresentationSsoTicketIdentifyingStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      List<SsoTicketExtractorConfig> ticketExtractors,
      TicketDecoder ticketDecoder,
      @NonNull AcceptedSsoTicketRepositoryConfig acceptedSsoTicketRepository,
      String usernameTicketKey,
      List<SsoTicketTagExtractorConfig> ticketTagExtractors,
      List<SsoTicketRoleExtractorConfig> ticketRoleExtractors,
      List<SsoTicketContextDataExtractorConfig> ticketContextDataExtractors,
      String authenticationMethodId,
      boolean allowLockedUsers
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes,
         ticketExtractors,
         ticketDecoder,
         acceptedSsoTicketRepository,
         usernameTicketKey,
         ticketTagExtractors,
         ticketRoleExtractors,
         ticketContextDataExtractors
      );
      if (acceptedSsoTicketRepository == null) {
         throw new NullPointerException("acceptedSsoTicketRepository is marked non-null but is null");
      }

      this.authenticationMethodId = authenticationMethodId;
      this.allowLockedUsers = allowLockedUsers;
   }

   public boolean isAllowLockedUsers() {
      return this.allowLockedUsers;
   }

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

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class RepresentationSsoTicketIdentifyingStepConfigBuilder {
      @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 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<SsoTicketExtractorConfig> ticketExtractors;
      @SuppressFBWarnings(justification = "generated code")
      private TicketDecoder ticketDecoder;
      @SuppressFBWarnings(justification = "generated code")
      private AcceptedSsoTicketRepositoryConfig acceptedSsoTicketRepository;
      @SuppressFBWarnings(justification = "generated code")
      private String usernameTicketKey;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<SsoTicketTagExtractorConfig> ticketTagExtractors;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<SsoTicketRoleExtractorConfig> ticketRoleExtractors;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<SsoTicketContextDataExtractorConfig> ticketContextDataExtractors;
      @SuppressFBWarnings(justification = "generated code")
      private String authenticationMethodId;
      @SuppressFBWarnings(justification = "generated code")
      private boolean allowLockedUsers;

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

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

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

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

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

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

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

         return this;
      }

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketExtractor(
         SsoTicketExtractorConfig ticketExtractor
      ) {
         if (this.ticketExtractors == null) {
            this.ticketExtractors = new ArrayList<>();
         }

         this.ticketExtractors.add(ticketExtractor);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketExtractors(
         Collection<? extends SsoTicketExtractorConfig> ticketExtractors
      ) {
         if (ticketExtractors == null) {
            throw new NullPointerException("ticketExtractors cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder clearTicketExtractors() {
         if (this.ticketExtractors != null) {
            this.ticketExtractors.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketDecoder(TicketDecoder ticketDecoder) {
         this.ticketDecoder = ticketDecoder;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder acceptedSsoTicketRepository(
         @NonNull AcceptedSsoTicketRepositoryConfig acceptedSsoTicketRepository
      ) {
         if (acceptedSsoTicketRepository == null) {
            throw new NullPointerException("acceptedSsoTicketRepository is marked non-null but is null");
         }

         this.acceptedSsoTicketRepository = acceptedSsoTicketRepository;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder usernameTicketKey(String usernameTicketKey) {
         this.usernameTicketKey = usernameTicketKey;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketTagExtractor(
         SsoTicketTagExtractorConfig ticketTagExtractor
      ) {
         if (this.ticketTagExtractors == null) {
            this.ticketTagExtractors = new ArrayList<>();
         }

         this.ticketTagExtractors.add(ticketTagExtractor);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketTagExtractors(
         Collection<? extends SsoTicketTagExtractorConfig> ticketTagExtractors
      ) {
         if (ticketTagExtractors == null) {
            throw new NullPointerException("ticketTagExtractors cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder clearTicketTagExtractors() {
         if (this.ticketTagExtractors != null) {
            this.ticketTagExtractors.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketRoleExtractor(
         SsoTicketRoleExtractorConfig ticketRoleExtractor
      ) {
         if (this.ticketRoleExtractors == null) {
            this.ticketRoleExtractors = new ArrayList<>();
         }

         this.ticketRoleExtractors.add(ticketRoleExtractor);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketRoleExtractors(
         Collection<? extends SsoTicketRoleExtractorConfig> ticketRoleExtractors
      ) {
         if (ticketRoleExtractors == null) {
            throw new NullPointerException("ticketRoleExtractors cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder clearTicketRoleExtractors() {
         if (this.ticketRoleExtractors != null) {
            this.ticketRoleExtractors.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketContextDataExtractor(
         SsoTicketContextDataExtractorConfig ticketContextDataExtractor
      ) {
         if (this.ticketContextDataExtractors == null) {
            this.ticketContextDataExtractors = new ArrayList<>();
         }

         this.ticketContextDataExtractors.add(ticketContextDataExtractor);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder ticketContextDataExtractors(
         Collection<? extends SsoTicketContextDataExtractorConfig> ticketContextDataExtractors
      ) {
         if (ticketContextDataExtractors == null) {
            throw new NullPointerException("ticketContextDataExtractors cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder clearTicketContextDataExtractors() {
         if (this.ticketContextDataExtractors != null) {
            this.ticketContextDataExtractors.clear();
         }

         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder allowLockedUsers(boolean allowLockedUsers) {
         this.allowLockedUsers = allowLockedUsers;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RepresentationSsoTicketIdentifyingStepConfig 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 ticketExtractors = switch (this.ticketExtractors == null ? 0 : this.ticketExtractors.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.ticketExtractors.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.ticketExtractors));
         };

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

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

         return new RepresentationSsoTicketIdentifyingStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            tagsOnSuccess,
            this.stepId,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            ticketExtractors,
            this.ticketDecoder,
            this.acceptedSsoTicketRepository,
            this.usernameTicketKey,
            ticketTagExtractors,
            ticketRoleExtractors,
            switch (this.ticketContextDataExtractors == null ? 0 : this.ticketContextDataExtractors.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.ticketContextDataExtractors.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.ticketContextDataExtractors));
            },
            this.authenticationMethodId,
            this.allowLockedUsers
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "RepresentationSsoTicketIdentifyingStepConfig.RepresentationSsoTicketIdentifyingStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", ticketExtractors="
            + this.ticketExtractors
            + ", ticketDecoder="
            + this.ticketDecoder
            + ", acceptedSsoTicketRepository="
            + this.acceptedSsoTicketRepository
            + ", usernameTicketKey="
            + this.usernameTicketKey
            + ", ticketTagExtractors="
            + this.ticketTagExtractors
            + ", ticketRoleExtractors="
            + this.ticketRoleExtractors
            + ", ticketContextDataExtractors="
            + this.ticketContextDataExtractors
            + ", authenticationMethodId="
            + this.authenticationMethodId
            + ", allowLockedUsers="
            + this.allowLockedUsers
            + ")";
      }
   }
}
