package com.airlock.iam.publicselfservice.application.configuration;

import com.airlock.iam.flow.application.configuration.validation.FlowIdConfigValidator;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
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.PropertyConfigurationException;
import com.airlock.iam.publicselfservice.application.configuration.flow.PublicSelfServiceFlowConfig;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Plugin(description = "Settings for flow-based public self-services via REST.", displayName = "Public Self-Service Flows")
public final class PublicSelfServiceFlowsConfig {
   @PluginListProperty(description = "The list of available public self-service flows.", minElements = 1, optional = false)
   private List<PublicSelfServiceFlowConfig> flows;
   public static final String FLOWS_PROPERTY = "flows";
   @IntegerProperty(
      displayName = "Max Failed Factor Attempts",
      description = "<p>Maximal number of allowed failed factor attempts. The user is locked if the number of failed attempts for any factor exceeds this limit.</p><p><b>Security Warning:</b> The \"Locked User Restriction\" must be configured in order to disallow public self-services for users that have exceeded the allowed number of factor attempts configured in this property. The \"Default Password Reset Restrictions \" already include this restriction.</p>",
      exampleValues = {0, 3, 5},
      minValue = 0,
      defaultValue = 3,
      optional = true
   )
   private int maxFailedAttempts;
   @IntegerProperty(
      description = "Defines the maximum number of unlocks that can be performed by the user without logging in successfully in between. If not configured, unlocks are not counted. Typically, this limit is only needed, if no password is set during the flow, i.e. if it is an unlock-only flow giving the user more attempts on the password check. To prevent users with too many unlocks from gaining further attempts, configure the restrictions of the flow in one of the following ways:<ul><li>Use the \"Default Self-Unlock Restrictions\"</li><li>Configure the \"Too Many Unlocks Restriction\" if using the \"Custom Public Self-Service Restrictions\"</li></ul>",
      displayName = "Max Number of Unlocks",
      exampleValues = 3,
      optional = true
   )
   private Integer maxNumberOfUnlocks;

   @ConfigurationValidator
   static void validateFlowIdsUnique(PluginConfiguration pc, ConfigurationEnvironment env, ConfigurationContext context, Environment valEnv) throws PropertyConfigurationException {
      new FlowIdConfigValidator(pc, env, context, "flows", "flowId").validateFlowIdsUniqueForFlows();
   }

   public List<PublicSelfServiceFlowConfig> getFlows() {
      return this.flows;
   }

   public int getMaxFailedAttempts() {
      return this.maxFailedAttempts;
   }

   public Integer getMaxNumberOfUnlocks() {
      return this.maxNumberOfUnlocks;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public PublicSelfServiceFlowsConfig(List<PublicSelfServiceFlowConfig> flows, int maxFailedAttempts, Integer maxNumberOfUnlocks) {
      this.flows = flows;
      this.maxFailedAttempts = maxFailedAttempts;
      this.maxNumberOfUnlocks = maxNumberOfUnlocks;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class PublicSelfServiceFlowsConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<PublicSelfServiceFlowConfig> flows;
      @SuppressFBWarnings(justification = "generated code")
      private int maxFailedAttempts;
      @SuppressFBWarnings(justification = "generated code")
      private Integer maxNumberOfUnlocks;

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

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceFlowsConfig.PublicSelfServiceFlowsConfigBuilder flow(PublicSelfServiceFlowConfig flow) {
         if (this.flows == null) {
            this.flows = new ArrayList<>();
         }

         this.flows.add(flow);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceFlowsConfig.PublicSelfServiceFlowsConfigBuilder flows(Collection<? extends PublicSelfServiceFlowConfig> flows) {
         if (flows == null) {
            throw new NullPointerException("flows cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceFlowsConfig.PublicSelfServiceFlowsConfigBuilder clearFlows() {
         if (this.flows != null) {
            this.flows.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceFlowsConfig.PublicSelfServiceFlowsConfigBuilder maxFailedAttempts(int maxFailedAttempts) {
         this.maxFailedAttempts = maxFailedAttempts;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceFlowsConfig.PublicSelfServiceFlowsConfigBuilder maxNumberOfUnlocks(Integer maxNumberOfUnlocks) {
         this.maxNumberOfUnlocks = maxNumberOfUnlocks;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceFlowsConfig build() {
         return new PublicSelfServiceFlowsConfig(switch (this.flows == null ? 0 : this.flows.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.flows.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.flows));
         }, this.maxFailedAttempts, this.maxNumberOfUnlocks);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "PublicSelfServiceFlowsConfig.PublicSelfServiceFlowsConfigBuilder(flows="
            + this.flows
            + ", maxFailedAttempts="
            + this.maxFailedAttempts
            + ", maxNumberOfUnlocks="
            + this.maxNumberOfUnlocks
            + ")";
      }
   }
}
