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

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.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.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 password reset flows. Currently, they are (in this order):<ul><li>Nonexistent User Restriction</li><li>Invalid User Restriction</li><li>Locked User 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 Password Reset Restrictions"
)
@Groups(groupSequence = {"Policy Settings", "Feedback Settings"}, openGroups = {"Policy Settings", "Feedback Settings"})
public final class DefaultPasswordResetRestrictionsConfig implements PublicSelfServiceRestrictionsConfig {
   public static final String DEFAULT_PASSWORD_RESET_RESTRICTIONS_DISPLAY_NAME = "Default Password Reset Restrictions";
   static final String POLICY_SETTINGS = "Policy Settings";
   static final String FEEDBACK_SETTINGS = "Feedback Settings";
   @BooleanProperty(
      description = "<p>If enabled, users that are locked because of too many failed password checks (lock reason \"LockReason.TooManyAuthAtts.PASSWORD\") are allowed to perform a password reset flow. Locked users with any other lock reason cannot reset their password. To allow other lock reasons, use the \"Locked User Restriction\" plugin with \"Custom Public Self-Service Restrictions\".</p>",
      optional = true,
      defaultValue = "false"
   )
   @GroupProperty(groupIdentifier = "Policy Settings")
   private boolean allowLockedUser;
   @BooleanProperty(
      description = "<p>If enabled, nonexistent users attempting to start a password reset 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"
   )
   @GroupProperty(groupIdentifier = "Feedback Settings")
   private boolean nonexistentUserFeedback;
   @BooleanProperty(
      description = "<p>If enabled, invalid users attempting to start a password reset 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"
   )
   @GroupProperty(groupIdentifier = "Feedback Settings")
   private boolean invalidUserFeedback;
   @BooleanProperty(
      description = "<p>If enabled, locked users attempting to start a password reset 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"
   )
   @GroupProperty(groupIdentifier = "Feedback Settings")
   private boolean lockedUserFeedback;
   private List<PublicSelfServiceRestrictionConfig> restrictions;

   public DefaultPasswordResetRestrictionsConfig(
      boolean allowLockedUser, boolean nonexistentUserFeedback, boolean invalidUserFeedback, boolean lockedUserFeedback
   ) {
      this.allowLockedUser = allowLockedUser;
      this.nonexistentUserFeedback = nonexistentUserFeedback;
      this.invalidUserFeedback = invalidUserFeedback;
      this.lockedUserFeedback = lockedUserFeedback;
   }

   @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.allowLockedUser ? List.of("LockReason.TooManyAuthAtts.PASSWORD") : List.of())
            .enableFeedback(this.lockedUserFeedback)
            .build()
      );
   }

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

   public boolean isAllowLockedUser() {
      return this.allowLockedUser;
   }

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

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

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

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

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class DefaultPasswordResetRestrictionsConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private boolean allowLockedUser;
      @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")
      DefaultPasswordResetRestrictionsConfigBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultPasswordResetRestrictionsConfig.DefaultPasswordResetRestrictionsConfigBuilder allowLockedUser(boolean allowLockedUser) {
         this.allowLockedUser = allowLockedUser;
         return this;
      }

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "DefaultPasswordResetRestrictionsConfig.DefaultPasswordResetRestrictionsConfigBuilder(allowLockedUser="
            + this.allowLockedUser
            + ", nonexistentUserFeedback="
            + this.nonexistentUserFeedback
            + ", invalidUserFeedback="
            + this.invalidUserFeedback
            + ", lockedUserFeedback="
            + this.lockedUserFeedback
            + ")";
      }
   }
}
