package com.airlock.iam.flow.shared.application.configuration.emailotp;

import com.airlock.iam.common.api.application.configuration.valueprovider.StringValueProviderConfig;
import com.airlock.iam.common.api.domain.service.email.EmailService;
import com.airlock.iam.common.application.configuration.masking.MaskingConfig;
import com.airlock.iam.core.misc.authen.SecretStringGenerator;
import com.airlock.iam.core.misc.impl.authen.ExtendedStringGenerator;
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.api.application.configuration.message.EmailMessageProviderConfig;
import com.airlock.iam.flow.shared.domain.model.emailotp.EmailOtpCheckingStep;
import com.airlock.iam.plugin.api.annotation.config.NextActions;
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.annotation.property.StringProperty;
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;

@NextActions(EmailOtpCheckingStep.EmailOtpStepActions.class)
public abstract class CommonEmailOtpCheckingStepConfig extends AbstractInteractiveFlowStepConfig {
   @PluginProperty(
      description = "Email service plugin. This defines what mail server is used for sending the email. It also defines the sender address and whether the email should be signed or not.",
      optional = false
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private EmailService emailService;
   @PluginProperty(description = "Creates the Email message content.", displayName = "Email Message Provider", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private EmailMessageProviderConfig emailMessageProvider;
   @PluginProperty(
      description = "A String Value Provider which provides the email address of the recipient. Make sure that this\nalways provides a valid email address, otherwise this step will fail.\n",
      optional = false
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private StringValueProviderConfig recipientAddress;
   @BooleanProperty(
      description = "Enable if the message template is HTML code.",
      optional = true,
      defaultValue = "true",
      displayName = "Message Template Is HTML"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean messageTemplateIsHtml;
   @BooleanProperty(description = "If enabled, the case of characters is ignored when checking OTPs.", defaultValue = "false", optional = true)
   @GroupProperty(groupIdentifier = "Security Settings")
   private boolean ignoreCase;
   @PluginProperty(
      description = "Settings for masking the email address in the REST API responses. Please refer to the REST API documentation\nfor further details. <p>If left empty, the email address will not be masked.</p>",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private MaskingConfig maskingSettings;
   @PluginProperty(
      description = "The string generator plugin to generate the OTP.",
      optional = true,
      defaultValue = ExtendedStringGenerator.class,
      displayName = "OTP Generator"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private SecretStringGenerator otpGenerator;
   @IntegerProperty(
      description = "Determines how long the OTP is valid (in seconds).",
      displayName = "OTP Validity [s]",
      defaultValue = 300,
      minValue = 1,
      optional = true,
      exampleValues = {60, 300, 600}
   )
   @GroupProperty(groupIdentifier = "Security Settings")
   private int otpValiditySeconds;
   @IntegerProperty(
      description = "<p>The number of times the user may enter a wrong OTP before the flow is aborted.\nIf set to zero (the default), only one attempt is possible.</p>\n<p>The purpose of this settings is usability. The failed attempts counter is always increased upon\nreceiving a wrong OTP and the user is locked when the global failed attempts limit is exceeded.</p>",
      minValue = 0,
      defaultValue = 0,
      optional = true,
      exampleValues = {0, 3, 5}
   )
   @GroupProperty(groupIdentifier = "Security Settings")
   private int maxRetries;
   @IntegerProperty(description = "The number of times the email can be resent.", minValue = 0, defaultValue = 0, optional = true, exampleValues = {0, 1, 2})
   @GroupProperty(groupIdentifier = "Security Settings")
   private int maxResends;
   @StringProperty(
      description = "The identifier of the authentication method for this step. Since the authentication method is also the identifier for failed login attempts for this step, distinct identifiers must be chosen if multiple instances of the same step type are used to check different credentials (e.g., two password step instances check two different passwords).<p>Example: a system tracks two passwords per user. Password 1 is used in flow A, password 2 in flow B. These two steps must have distinct 'Authentication Method Ids', e.g. PASSWORD1 and PASSWORD2.</p>If only one identifier (e.g. \"PASSWORD\") is used, this may allow brute force attacks on the password as follows. Assuming an attacker knows the user's password 1, they get an unlimited number of attempts on flow B, as the 'PASSWORD' counter can repeatedly be reset to 0 by performing a successful login on flow A.<p>To prevent such attacks, use two different counters by setting the authentication methods, for example, to PASSWORD1 and PASSWORD2, respectively.</p>",
      displayName = "Authentication Method ID",
      defaultValue = "EMAIL",
      maxLength = 23,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private String authenticationMethodId;

   public Optional<MaskingConfig> getMaskingSettings() {
      return Optional.ofNullable(this.maskingSettings);
   }

   public Duration getOtpValidty() {
      return Duration.ofSeconds(this.otpValiditySeconds);
   }

   protected CommonEmailOtpCheckingStepConfig(
      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,
      EmailService emailService,
      EmailMessageProviderConfig emailMessageProvider,
      StringValueProviderConfig recipientAddress,
      boolean messageTemplateIsHtml,
      boolean ignoreCase,
      int otpValiditySeconds,
      int maxRetries,
      int maxResends,
      SecretStringGenerator otpGenerator,
      String authenticationMethodId,
      MaskingConfig maskingSettings
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.emailService = emailService;
      this.emailMessageProvider = emailMessageProvider;
      this.recipientAddress = recipientAddress;
      this.messageTemplateIsHtml = messageTemplateIsHtml;
      this.ignoreCase = ignoreCase;
      this.otpValiditySeconds = otpValiditySeconds;
      this.maxRetries = maxRetries;
      this.maxResends = maxResends;
      this.otpGenerator = otpGenerator;
      this.authenticationMethodId = authenticationMethodId;
      this.maskingSettings = maskingSettings;
   }

   public EmailService getEmailService() {
      return this.emailService;
   }

   public EmailMessageProviderConfig getEmailMessageProvider() {
      return this.emailMessageProvider;
   }

   public StringValueProviderConfig getRecipientAddress() {
      return this.recipientAddress;
   }

   public boolean isMessageTemplateIsHtml() {
      return this.messageTemplateIsHtml;
   }

   public boolean isIgnoreCase() {
      return this.ignoreCase;
   }

   public SecretStringGenerator getOtpGenerator() {
      return this.otpGenerator;
   }

   public int getMaxRetries() {
      return this.maxRetries;
   }

   public int getMaxResends() {
      return this.maxResends;
   }

   public String getAuthenticationMethodId() {
      return this.authenticationMethodId;
   }

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