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

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.StringListProperty;
import com.airlock.iam.plugin.framework.application.service.builder.plugin.PluginPropertyBuilderInitializer;
import com.airlock.iam.publicselfservice.api.application.configuration.PublicSelfServiceRestrictionConfig;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;

@Plugin(
   description = "<p>Default restrictions for self-unlock flows. Currently, they are (in this order):<ul><li>Nonexistent User Restriction</li><li>Invalid User Restriction</li><li>Locked User Restriction (only users with one of the configured \"Allowed Lock Reasons\" are allowed to perform self-unlock. Users that are not locked may still perform a self-unlock flow.)</li><li>Too Many Unlocks Restriction</li></ul></p><p>By default, these restrictions do not report violations and therefore prevent user enumeration. To include custom restrictions or to configure restrictions for other types of self-service flows, the \"Custom Public Self-Service Restrictions\" plugin can be used.</p>",
   displayName = "Default Self-Unlock Restrictions"
)
public final class DefaultSelfUnlockRestrictionsConfig implements PublicSelfServiceRestrictionsConfig {
   public static final String DEFAULT_SELF_UNLOCK_RESTRICTIONS_DISPLAY_NAME = "Default Self-Unlock Restrictions";
   @BooleanProperty(
      description = "<p>If enabled, nonexistent users attempting to start a self-unlock flow receive an error response with error code \"USER_NOT_FOUND\". Otherwise, the flow would also continue for nonexistent users but fail in the verification step (to protect against user enumeration).</p><p><b>Security consideration:</b> Enabling feedback on restriction violations can increase usability but it weakens or disables the user enumeration protection.</p>",
      optional = true,
      defaultValue = "false"
   )
   private boolean nonexistentUserFeedback;
   @BooleanProperty(
      description = "<p>If enabled, invalid users attempting to start a self-unlock flow receive an error response with error code \"USER_INVALID\". Otherwise, the flow would also continue for invalid users, but fail in the verification step (to protect against user enumeration).</p><p><b>Security consideration:</b> Enabling feedback on restriction violations can increase usability but it weakens or disables the user enumeration protection.</p>",
      optional = true,
      defaultValue = "false"
   )
   private boolean invalidUserFeedback;
   @BooleanProperty(
      description = "<p>If enabled, locked users attempting to start a self-unlock flow receive an error response with error code \"USER_LOCKED\". Otherwise, the flow would also continue for locked users but fail in the verification step (to protect against user enumeration).</p><p><b>Security consideration:</b> Enabling feedback on restriction violations can increase usability but it weakens or disables the user enumeration protection.</p>",
      optional = true,
      defaultValue = "false"
   )
   private boolean lockedUserFeedback;
   @BooleanProperty(
      description = "<p>If enabled and the 'Public Self-Service Flow Settings' configuration has a 'Max Number of Unlocks' limit configured, locked users with more unlock attempts attempting to start a self-unlock flow receive an error response with error code \"TOO_MANY_UNLOCKS\". Otherwise, the flow would also continue for locked users but fail in the verification step (to protect against user enumeration).</p><p><b>Security consideration:</b> Enabling feedback on restriction violations can increase usability but it weakens or disables the user enumeration protection.</p>",
      optional = true,
      defaultValue = "false"
   )
   private boolean tooManyUnlockFeedback;
   @StringListProperty(
      description = "<p>List of lock reasons that still <em>allow</em> the user to perform the self-unlock. Locked users with any lock reason not listed here will be rejected.</p><p>Note that a user is not automatically unlocked after a successful public self-service. A \"Unlock User Step (Public Self-Service)\" step has to be configured to perform this task.</p>",
      exampleElements = {"LockReason.TooManyAuthAtts.PASSWORD", "LockReason.PendingChannelVerification"},
      defaultValue = "LockReason.TooManyAuthAtts.PASSWORD",
      optional = true,
      displayName = "Allowed Lock Reasons",
      unique = true,
      minElements = 1
   )
   private List<String> allowedLockReasons;
   private static final String ALLOWED_LOCK_REASONS_DISPLAY_NAME = "Allowed Lock Reasons";
   private List<PublicSelfServiceRestrictionConfig> restrictions;

   public DefaultSelfUnlockRestrictionsConfig(
      boolean nonexistentUserFeedback, boolean invalidUserFeedback, boolean lockedUserFeedback, boolean tooManyUnlockFeedback, List<String> allowedLockReasons
   ) {
      this.nonexistentUserFeedback = nonexistentUserFeedback;
      this.invalidUserFeedback = invalidUserFeedback;
      this.lockedUserFeedback = lockedUserFeedback;
      this.tooManyUnlockFeedback = tooManyUnlockFeedback;
      this.allowedLockReasons = allowedLockReasons;
   }

   @AfterConfigActivation
   void initializeRestrictions() {
      this.restrictions = List.of(
         pluginBuilder(NonexistentUserRestrictionConfig.NonexistentUserRestrictionConfigBuilder.class).enableFeedback(this.nonexistentUserFeedback).build(),
         pluginBuilder(InvalidUserRestrictionConfig.InvalidUserRestrictionConfigBuilder.class).enableFeedback(this.invalidUserFeedback).build(),
         pluginBuilder(LockedUserRestrictionConfig.LockedUserRestrictionConfigBuilder.class)
            .allowedLockReasons(this.allowedLockReasons)
            .enableFeedback(this.lockedUserFeedback)
            .build(),
         pluginBuilder(TooManyUnlocksRestrictionConfig.TooManyUnlocksRestrictionConfigBuilder.class).enableFeedback(this.tooManyUnlockFeedback).build()
      );
   }

   private static <T> T pluginBuilder(Class<T> builderClass) {
      return (T)PluginPropertyBuilderInitializer.builder(builderClass);
   }

   public boolean isNonexistentUserFeedback() {
      return this.nonexistentUserFeedback;
   }

   public boolean isInvalidUserFeedback() {
      return this.invalidUserFeedback;
   }

   public boolean isLockedUserFeedback() {
      return this.lockedUserFeedback;
   }

   public boolean isTooManyUnlockFeedback() {
      return this.tooManyUnlockFeedback;
   }

   public List<String> getAllowedLockReasons() {
      return this.allowedLockReasons;
   }

   @Override
   public List<PublicSelfServiceRestrictionConfig> getRestrictions() {
      return this.restrictions;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class DefaultSelfUnlockRestrictionsConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private boolean nonexistentUserFeedback;
      @SuppressFBWarnings(justification = "generated code")
      private boolean invalidUserFeedback;
      @SuppressFBWarnings(justification = "generated code")
      private boolean lockedUserFeedback;
      @SuppressFBWarnings(justification = "generated code")
      private boolean tooManyUnlockFeedback;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> allowedLockReasons;

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

      @SuppressFBWarnings(justification = "generated code")
      public DefaultSelfUnlockRestrictionsConfig.DefaultSelfUnlockRestrictionsConfigBuilder nonexistentUserFeedback(boolean nonexistentUserFeedback) {
         this.nonexistentUserFeedback = nonexistentUserFeedback;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultSelfUnlockRestrictionsConfig.DefaultSelfUnlockRestrictionsConfigBuilder invalidUserFeedback(boolean invalidUserFeedback) {
         this.invalidUserFeedback = invalidUserFeedback;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultSelfUnlockRestrictionsConfig.DefaultSelfUnlockRestrictionsConfigBuilder lockedUserFeedback(boolean lockedUserFeedback) {
         this.lockedUserFeedback = lockedUserFeedback;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultSelfUnlockRestrictionsConfig.DefaultSelfUnlockRestrictionsConfigBuilder tooManyUnlockFeedback(boolean tooManyUnlockFeedback) {
         this.tooManyUnlockFeedback = tooManyUnlockFeedback;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultSelfUnlockRestrictionsConfig.DefaultSelfUnlockRestrictionsConfigBuilder allowedLockReasons(List<String> allowedLockReasons) {
         this.allowedLockReasons = allowedLockReasons;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultSelfUnlockRestrictionsConfig build() {
         return new DefaultSelfUnlockRestrictionsConfig(
            this.nonexistentUserFeedback, this.invalidUserFeedback, this.lockedUserFeedback, this.tooManyUnlockFeedback, this.allowedLockReasons
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "DefaultSelfUnlockRestrictionsConfig.DefaultSelfUnlockRestrictionsConfigBuilder(nonexistentUserFeedback="
            + this.nonexistentUserFeedback
            + ", invalidUserFeedback="
            + this.invalidUserFeedback
            + ", lockedUserFeedback="
            + this.lockedUserFeedback
            + ", tooManyUnlockFeedback="
            + this.tooManyUnlockFeedback
            + ", allowedLockReasons="
            + this.allowedLockReasons
            + ")";
      }
   }
}
