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

import com.airlock.iam.flow.shared.domain.model.newdevice.LoginFromNewDeviceEncryptionKey;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
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 com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import com.airlock.iam.plugin.framework.domain.service.validation.DurationValidator;
import com.airlock.iam.plugin.framework.misc.processor.PasswordPropertyProcessor;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;

@Plugin(description = "Configures the cookie used for detecting a login from a new device.")
public final class NewDeviceCookieConfig {
   @StringProperty(
      description = "The name of the cookie that is used to detect whether a login from a new device happened.<p>If this name is changed, the IAM Loginapp mapping on Airlock Gateway has to be reconfigured to pass-through this cookie.</p>",
      optional = true,
      minLength = 1,
      maxLength = 30,
      validationRegex = "[a-zA-Z0-9_]+",
      defaultValue = "AL_LoginFromNewDevice"
   )
   private String cookieName;
   @StringProperty(
      description = "<p>The key used to encrypt the cookie. Must be 32 Bytes encoded as Base64 String.</p><p>One can, for example, generate a random base64 string with 256 bits (32 bytes) using OpenSSL as follows: <code>openssl rand -base64 32</code></p><p>If this value is changed, all previously used devices are forgotten.</p>",
      optional = false,
      minLength = 44,
      maxLength = 44,
      isSensitive = true
   )
   private String key;
   public static final String KEY_FIELD = "key";
   @StringProperty(
      description = "Time period during which previous logins are considered. If a user's most recent login on a device is older than the configured validity period, the device is treated as new for that user.",
      optional = true,
      defaultValue = "365d",
      exampleValues = {"10d", "8h", "2d 12h"}
   )
   private String validity;
   private static final String VALIDITY_FIELD = "validity";

   public String getCookieName() {
      return this.cookieName;
   }

   public String getKey() {
      return this.key;
   }

   public String getValidity() {
      return this.validity;
   }

   @ConfigurationValidator
   public static void checkProperties(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment) throws ConfigurationException {
      DurationValidator.validateDurationFieldIfSet(config, "validity", env, context);
      if (validationEnvironment == Environment.CURRENT_VM || !PasswordPropertyProcessor.isExternallyStored(config, "key", env, context)) {
         String key = StringPropertyValueReader.effectiveStringConfigValue(config, "key", env, context);
         Optional<String> validationError = LoginFromNewDeviceEncryptionKey.validate(key);
         if (validationError.isPresent()) {
            throw new PropertyConstraintViolationException("key", config, env, context, "Invalid encryption key");
         }
      }
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public NewDeviceCookieConfig(String cookieName, String key, String validity) {
      this.cookieName = cookieName;
      this.key = key;
      this.validity = validity;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class NewDeviceCookieConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String cookieName;
      @SuppressFBWarnings(justification = "generated code")
      private String key;
      @SuppressFBWarnings(justification = "generated code")
      private String validity;

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

      @SuppressFBWarnings(justification = "generated code")
      public NewDeviceCookieConfig.NewDeviceCookieConfigBuilder cookieName(String cookieName) {
         this.cookieName = cookieName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public NewDeviceCookieConfig.NewDeviceCookieConfigBuilder key(String key) {
         this.key = key;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public NewDeviceCookieConfig.NewDeviceCookieConfigBuilder validity(String validity) {
         this.validity = validity;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public NewDeviceCookieConfig build() {
         return new NewDeviceCookieConfig(this.cookieName, this.key, this.validity);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "NewDeviceCookieConfig.NewDeviceCookieConfigBuilder(cookieName=" + this.cookieName + ", key=" + this.key + ", validity=" + this.validity + ")";
      }
   }
}
