package com.airlock.iam.common.application.configuration.templock;

import com.airlock.iam.common.api.application.configuration.locking.TemporaryLockingStrategyConfig;
import com.airlock.iam.common.api.domain.model.locking.TemporaryLockingStrategy;
import com.airlock.iam.common.domain.model.templock.ExponentialTemporaryLockingStrategy;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.DoubleProperty;
import com.airlock.iam.plugin.api.annotation.property.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.TestletProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

@Plugin(
   displayName = "Exponential Temporary Locking Strategy",
   description = "A Temporary Locking strategy with a duration based on an exponential or linear formula. The lock duration after <i>n</i> failed attempts is calculated as follows:<p><i>Duration = (Base Duration) * (Exponential Factor) ^ (n - 1) + (Additional Duration) * (n - 1)</i></p>Note that the first addend is always greater than <i>Base Duration</i><br/>"
)
public class ExponentialTemporaryLockingStrategyConfig implements TemporaryLockingStrategyConfig {
   @IntegerProperty(
      displayName = "Base Duration (in ms)",
      description = "The duration (in milliseconds) a user is locked out after exactly one failed authentication attempt.",
      optional = true,
      exampleValues = {500, 3000, 5000},
      minValue = 0,
      defaultValue = 3000
   )
   @TestletProperty(testletClass = ExponentialTemporaryLockingStrategyConfigTestlet.class, buttonToolTip = "Show the resulting durations")
   private int baseDurationMs;
   static final String BASE_DURATION_MS_FIELD = "baseDurationMs";
   @DoubleProperty(
      displayName = "Exponential Factor",
      description = "Factor that is multiplied to <i>Base Duration</i> with each failed attempt. If set to 1.0, the duration only grows linearly with the number of failed attempts.<br/>After one failed attempt, <i>Base Duration</i> is used. After two failed attempts, <i>Base Duration</i> is multiplied with this factor. After three failed attempts, this factor is multiplied once more. For example, if the factor is 2.0, the duration is doubled with every failed attempt.<br/>Note that this property can be combined with <i>Additional Duration</i>. See the plugin description for the overall formula.",
      optional = true,
      exampleValues = {1.0, 1.5, 2.0},
      minValue = 1.0,
      maxValue = 1000.0,
      defaultValue = 2.0
   )
   private double exponentialFactor;
   static final String EXPONENTIAL_FACTOR_FIELD = "exponentialFactor";
   @IntegerProperty(
      displayName = "Additional Duration (in ms)",
      description = "Duration (in milliseconds) that is added to <i>Base Duration</i> with each failed attempt. It has an effect if greater than 0 ms.<br/>After one failed attempt, <i>Base Duration</i> is used. After two failed attempts, the additional duration is added to <i>Base Duration</i>. After three failed attempts, the additional duration is added once more.<br/>Note that this property can be combined with <i>Exponential Factor</i>. See the plugin description for the overall formula.",
      optional = true,
      exampleValues = {0, 1000, 2000},
      minValue = 0,
      defaultValue = 0
   )
   private int additionalDurationMs;
   static final String ADDITIONAL_DURATION_MS_FIELD = "additionalDurationMs";

   @Override
   public TemporaryLockingStrategy getLogic() {
      return new ExponentialTemporaryLockingStrategy(this);
   }

   public int getBaseDurationMs() {
      return this.baseDurationMs;
   }

   public double getExponentialFactor() {
      return this.exponentialFactor;
   }

   public int getAdditionalDurationMs() {
      return this.additionalDurationMs;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public ExponentialTemporaryLockingStrategyConfig(int baseDurationMs, double exponentialFactor, int additionalDurationMs) {
      this.baseDurationMs = baseDurationMs;
      this.exponentialFactor = exponentialFactor;
      this.additionalDurationMs = additionalDurationMs;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class ExponentialTemporaryLockingStrategyConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private int baseDurationMs;
      @SuppressFBWarnings(justification = "generated code")
      private double exponentialFactor;
      @SuppressFBWarnings(justification = "generated code")
      private int additionalDurationMs;

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

      @SuppressFBWarnings(justification = "generated code")
      public ExponentialTemporaryLockingStrategyConfig.ExponentialTemporaryLockingStrategyConfigBuilder baseDurationMs(int baseDurationMs) {
         this.baseDurationMs = baseDurationMs;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ExponentialTemporaryLockingStrategyConfig.ExponentialTemporaryLockingStrategyConfigBuilder exponentialFactor(double exponentialFactor) {
         this.exponentialFactor = exponentialFactor;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ExponentialTemporaryLockingStrategyConfig.ExponentialTemporaryLockingStrategyConfigBuilder additionalDurationMs(int additionalDurationMs) {
         this.additionalDurationMs = additionalDurationMs;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ExponentialTemporaryLockingStrategyConfig build() {
         return new ExponentialTemporaryLockingStrategyConfig(this.baseDurationMs, this.exponentialFactor, this.additionalDurationMs);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "ExponentialTemporaryLockingStrategyConfig.ExponentialTemporaryLockingStrategyConfigBuilder(baseDurationMs="
            + this.baseDurationMs
            + ", exponentialFactor="
            + this.exponentialFactor
            + ", additionalDurationMs="
            + this.additionalDurationMs
            + ")";
      }
   }
}
