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

import com.airlock.iam.factor.application.configuration.airlock2fa.Airlock2FASettings;
import com.airlock.iam.factor.application.configuration.airlock2fa.validation.Airlock2FAShortLivedQrCodeConfigValidator;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAShortLivedOnlineQrCodeSettings;
import com.airlock.iam.flow.api.application.configuration.dynamicactivation.DynamicStepActivationConfig;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.step.AbstractInteractiveFlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.InteractiveGotoTargetConfig;
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.flow.shared.application.configuration.airlock2fa.GenericAirlock2FAActivationStepConfig;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAActivationStep;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForAuthenticationFlows;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Groups;
import com.airlock.iam.plugin.api.annotation.config.NextActions;
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.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
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.ConfigurationException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Plugin(
   description = "<p>Step to add a new Airlock 2FA device. This step will generate a QR code (and an Airlock 2FA account if necessary) that needs to be scanned by the device to be added.</p><p>This Step allows device activation during the authentication flow even when the user already has a device. This step is able to add both the first device during a migration or an additional device.</p>",
   displayName = "Airlock 2FA Activation Step (with additional Activation)"
)
@PluginLicenseRestriction(moduleAffiliation = "Airlock2FA")
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Short-Lived Online QR Code Settings", "Tags/Guards", "Flow Control"})
@NextActions(Airlock2FAActivationStep.Airlock2FAActivationStepAction.class)
public class Airlock2FAAdditionalActivationStepConfig
   extends AbstractInteractiveFlowStepConfig
   implements StepConfigForAuthenticationFlows,
   GenericAirlock2FAActivationStepConfig {
   public static final String DISPLAY_NAME = "Airlock 2FA Activation Step (with additional Activation)";
   @PluginProperty(
      displayName = "Airlock 2FA Settings",
      description = "Settings of Airlock 2FA.",
      optional = false,
      globalPluginReference = "globalAirlock2FASettings"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private Airlock2FASettings airlock2faSettings;
   @IntegerProperty(
      displayName = "Enrollment Timeout [s]",
      description = "The duration (in seconds) an enrollment QR code should be valid.<p><b>Note:</b> This value is not used when generating activation letters.</p>",
      optional = true,
      exampleValues = 300,
      defaultValue = 300,
      minValue = 60,
      maxValue = 7776000
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private int enrollmentTimeoutSeconds;
   private static final String ENROLLMENT_TIMEOUT_FIELD_NAME = "enrollmentTimeoutSeconds";
   @BooleanProperty(
      defaultValue = "false",
      displayName = "Provide Short Activation Code",
      description = "If enabled, the activation step will provide a short activation code as an additional option to enroll a new device.<p><b>Note:</b> This feature cannot be used in combination with short-lived Online QR Codes.</p>",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean provideActivationCodeShort;
   @BooleanProperty(
      displayName = "Enable Short-Lived Online QR Codes",
      description = "<p>Whether to enable short-lived Online QR Codes. Unlike regular Online QR Codes, these are refreshed regularly, allowing for shorter individual validities.</p>\n<p>Shorter validities enhance security, since forwarding a QR code to victims and tricking them to scan the QR code becomes more difficult if the available time window is small.</p>",
      optional = true,
      defaultValue = "false"
   )
   @GroupProperty(groupIdentifier = "Short-Lived Online QR Code Settings")
   private boolean enableShortLivedOnlineQrCodes;
   @IntegerProperty(
      displayName = "QR Code Validity [s]",
      description = "<p>The maximum amount of time in seconds for which an Online QR Code is valid after it is first displayed to\nthe end user (ignoring latency). This duration includes the time defined for the validity overlap.\nIt only limits the time for scanning the QR code, not for the confirmation or approval afterwards.</p>\n<p><strong> Security Notice: The validity duration represents the attack window. Choosing a small validity makes\nattacks more difficult, in cases where an attacker attempts to forward a QR code to a victim for scanning.</strong></p>\n<p>This setting is only active if short-lived Online QR Codes are enabled.</p>",
      optional = true,
      defaultValue = 10,
      minValue = 3
   )
   @GroupProperty(groupIdentifier = "Short-Lived Online QR Code Settings")
   private int shortLivedQrCodeValidity;
   @IntegerProperty(
      displayName = "QR Code Validity Overlap [s]",
      description = "<p>Defines the duration in seconds during which the previously displayed QR Code is still valid after being\nreplaced by the next QR code in sequence.</p>\n<p>This provides time for pending requests to complete and ensures that a valid QR code is displayed at every\nmoment in time, provided that there are no network or performance issues.</p>\n<p>The validity overlap must meet the following criteria:</p>\n<ul>\n<li>It must be smaller than half the overall validity of the QR code.</li>\n<li>It must be larger than the Loginapp UI polling interval (1s) plus the network latency (IAM backend &rarr;\nLoginapp UI plus Mobile Device &rarr; Futurae Backend).<br/>\nNote that the polling interval may differ for custom user interfaces.</li>\n</ul>\n<p>This setting is only active if short-lived Online QR Codes are enabled.</p>",
      optional = true,
      defaultValue = 3,
      minValue = 2
   )
   @GroupProperty(groupIdentifier = "Short-Lived Online QR Code Settings")
   private int shortLivedQrCodeValidityOverlap;

   @ConfigurationValidator(localOnly = true)
   public static void validateActivationCodeShortProperty(
      PluginConfiguration currentConfig, ConfigurationEnvironment env, ConfigurationContext ctx, Environment vEnv
   ) throws ConfigurationException {
      GenericAirlock2FAActivationStepConfig.validateShortLivedSettingsProhibitActivationCodeShort(currentConfig, env, ctx);
   }

   @ConfigurationValidator(localOnly = true)
   public static void validateShortLivedQrCodeSettings(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext ctx, Environment vEnv) throws ConfigurationException {
      Airlock2FAShortLivedQrCodeConfigValidator.runShortLivedSettingsValidation("enrollmentTimeoutSeconds", Optional.empty(), config, env, ctx);
   }

   protected Airlock2FAAdditionalActivationStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      List<InteractiveGotoTargetConfig> interactiveGotoTargets,
      List<DynamicStepActivationConfig> dynamicStepActivations,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      Airlock2FASettings airlock2faSettings,
      String authenticationMethodId,
      int enrollmentTimeoutSeconds,
      boolean provideActivationCodeShort,
      boolean enableShortLivedOnlineQrCodes,
      int shortLivedQrCodeValidity,
      int shortLivedQrCodeValidityOverlap
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.airlock2faSettings = airlock2faSettings;
      this.enrollmentTimeoutSeconds = enrollmentTimeoutSeconds;
      this.provideActivationCodeShort = provideActivationCodeShort;
      this.enableShortLivedOnlineQrCodes = enableShortLivedOnlineQrCodes;
      this.shortLivedQrCodeValidity = shortLivedQrCodeValidity;
      this.shortLivedQrCodeValidityOverlap = shortLivedQrCodeValidityOverlap;
   }

   public Duration getEnrollmentTimeout() {
      return Duration.ofSeconds(this.enrollmentTimeoutSeconds);
   }

   public Airlock2FASettings getAirlock2faSettings() {
      return this.airlock2faSettings;
   }

   public boolean isProvideActivationCodeShortEnabled() {
      return this.provideActivationCodeShort;
   }

   public Optional<Airlock2FAShortLivedOnlineQrCodeSettings> getShortLivedOnlineQrCodeSettings() {
      return this.enableShortLivedOnlineQrCodes
         ? Optional.of(
            Airlock2FAShortLivedOnlineQrCodeSettings.shortLivedOnlineQrCodeSettings(
               this.shortLivedQrCodeValidity, this.shortLivedQrCodeValidityOverlap, this.enrollmentTimeoutSeconds
            )
         )
         : Optional.empty();
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class Airlock2FAAdditionalActivationStepConfigBuilder {
      @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 List<TagConfig> tagsOnSuccess;
      @SuppressFBWarnings(justification = "generated code")
      private StepIdConfig stepId;
      @SuppressFBWarnings(justification = "generated code")
      private List<InteractiveGotoTargetConfig> interactiveGotoTargets;
      @SuppressFBWarnings(justification = "generated code")
      private List<DynamicStepActivationConfig> dynamicStepActivations;
      @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 Airlock2FASettings airlock2faSettings;
      @SuppressFBWarnings(justification = "generated code")
      private String authenticationMethodId;
      @SuppressFBWarnings(justification = "generated code")
      private int enrollmentTimeoutSeconds;
      @SuppressFBWarnings(justification = "generated code")
      private boolean provideActivationCodeShort;
      @SuppressFBWarnings(justification = "generated code")
      private boolean enableShortLivedOnlineQrCodes;
      @SuppressFBWarnings(justification = "generated code")
      private int shortLivedQrCodeValidity;
      @SuppressFBWarnings(justification = "generated code")
      private int shortLivedQrCodeValidityOverlap;

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder tagsOnSuccess(List<TagConfig> tagsOnSuccess) {
         this.tagsOnSuccess = tagsOnSuccess;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder interactiveGotoTargets(
         List<InteractiveGotoTargetConfig> interactiveGotoTargets
      ) {
         this.interactiveGotoTargets = interactiveGotoTargets;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder dynamicStepActivations(
         List<DynamicStepActivationConfig> dynamicStepActivations
      ) {
         this.dynamicStepActivations = dynamicStepActivations;
         return this;
      }

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder airlock2faSettings(Airlock2FASettings airlock2faSettings) {
         this.airlock2faSettings = airlock2faSettings;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder enrollmentTimeoutSeconds(int enrollmentTimeoutSeconds) {
         this.enrollmentTimeoutSeconds = enrollmentTimeoutSeconds;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder provideActivationCodeShort(
         boolean provideActivationCodeShort
      ) {
         this.provideActivationCodeShort = provideActivationCodeShort;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder enableShortLivedOnlineQrCodes(
         boolean enableShortLivedOnlineQrCodes
      ) {
         this.enableShortLivedOnlineQrCodes = enableShortLivedOnlineQrCodes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder shortLivedQrCodeValidity(int shortLivedQrCodeValidity) {
         this.shortLivedQrCodeValidity = shortLivedQrCodeValidity;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder shortLivedQrCodeValidityOverlap(
         int shortLivedQrCodeValidityOverlap
      ) {
         this.shortLivedQrCodeValidityOverlap = shortLivedQrCodeValidityOverlap;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAAdditionalActivationStepConfig build() {
         return new Airlock2FAAdditionalActivationStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            this.tagsOnSuccess,
            this.stepId,
            this.interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.airlock2faSettings,
            this.authenticationMethodId,
            this.enrollmentTimeoutSeconds,
            this.provideActivationCodeShort,
            this.enableShortLivedOnlineQrCodes,
            this.shortLivedQrCodeValidity,
            this.shortLivedQrCodeValidityOverlap
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Airlock2FAAdditionalActivationStepConfig.Airlock2FAAdditionalActivationStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", interactiveGotoTargets="
            + this.interactiveGotoTargets
            + ", dynamicStepActivations="
            + this.dynamicStepActivations
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", airlock2faSettings="
            + this.airlock2faSettings
            + ", authenticationMethodId="
            + this.authenticationMethodId
            + ", enrollmentTimeoutSeconds="
            + this.enrollmentTimeoutSeconds
            + ", provideActivationCodeShort="
            + this.provideActivationCodeShort
            + ", enableShortLivedOnlineQrCodes="
            + this.enableShortLivedOnlineQrCodes
            + ", shortLivedQrCodeValidity="
            + this.shortLivedQrCodeValidity
            + ", shortLivedQrCodeValidityOverlap="
            + this.shortLivedQrCodeValidityOverlap
            + ")";
      }
   }
}
