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

import com.airlock.iam.common.domain.model.jwt.signature.JwsAlgorithmEc;
import com.airlock.iam.core.misc.persistency.token.TokenDataProvider;
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.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringListProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.time.Duration;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Plugin(
   description = "<p>Configures device token registration and authentication</p><p>Allows devices (e.g. mobile apps on smart phones) to register a public key (after successful authentication) and use the key for later authentication.<br/></p><p>For example, this can be used as follows: when logging in for the first time using the mobile app, the user authenticates using the password and a second factor. After this initial login, the mobile app registers its public key and obtains a device token. For later logins, the user authenticates using password only. The second factor can be omitted as long as the app has the device token.</p>"
)
@PluginLicenseRestriction(moduleAffiliation = "DeviceToken")
public class DeviceTokenSettings {
   @PluginProperty(
      description = "Token data provider used to load and store device token data.",
      optional = false,
      globalPluginReference = "globalTokenDataProvider"
   )
   private TokenDataProvider tokenDataProvider;
   @StringListProperty(
      description = "Allowed signature algorithms that can be used to authenticate with the device.",
      optional = true,
      minElements = 1,
      defaultValue = {"ES256", "ES256K", "ES384", "ES512"},
      exampleElements = {"ES256", "ES256K", "ES384", "ES512"},
      allowedValues = {"ES256", "ES256K", "ES384", "ES512"}
   )
   private List<String> allowedSignatureAlgorithms;
   @IntegerProperty(
      description = "<p>The validity period of a \"binding\", i.e. the amount of time a registered key is accepted for authentication after it has been registered. Afterwards, a registered key is no more accepted for authentication and a new key must be registered.</p><p>The validity is only relevant when the device token is registered. When using a device token, the expiry date that was computed at registration time is relevant.</p>",
      displayName = "Token Validity [days]",
      defaultValue = 30,
      minValue = 1,
      optional = true,
      exampleValues = {5, 30, 90, 365, 730}
   )
   private int tokenValidityDays;

   public Duration getTokenValidity() {
      return Duration.ofDays(this.tokenValidityDays);
   }

   public Set<JwsAlgorithmEc> getAllowedSignatureAlgorithms() {
      return this.allowedSignatureAlgorithms.stream().map(JwsAlgorithmEc::valueOf).collect(Collectors.toSet());
   }

   public TokenDataProvider getTokenDataProvider() {
      return this.tokenDataProvider;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public DeviceTokenSettings(TokenDataProvider tokenDataProvider, List<String> allowedSignatureAlgorithms, int tokenValidityDays) {
      this.tokenDataProvider = tokenDataProvider;
      this.allowedSignatureAlgorithms = allowedSignatureAlgorithms;
      this.tokenValidityDays = tokenValidityDays;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class DeviceTokenSettingsBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private TokenDataProvider tokenDataProvider;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> allowedSignatureAlgorithms;
      @SuppressFBWarnings(justification = "generated code")
      private int tokenValidityDays;

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

      @SuppressFBWarnings(justification = "generated code")
      public DeviceTokenSettings.DeviceTokenSettingsBuilder tokenDataProvider(TokenDataProvider tokenDataProvider) {
         this.tokenDataProvider = tokenDataProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DeviceTokenSettings.DeviceTokenSettingsBuilder allowedSignatureAlgorithms(List<String> allowedSignatureAlgorithms) {
         this.allowedSignatureAlgorithms = allowedSignatureAlgorithms;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DeviceTokenSettings.DeviceTokenSettingsBuilder tokenValidityDays(int tokenValidityDays) {
         this.tokenValidityDays = tokenValidityDays;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DeviceTokenSettings build() {
         return new DeviceTokenSettings(this.tokenDataProvider, this.allowedSignatureAlgorithms, this.tokenValidityDays);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "DeviceTokenSettings.DeviceTokenSettingsBuilder(tokenDataProvider="
            + this.tokenDataProvider
            + ", allowedSignatureAlgorithms="
            + this.allowedSignatureAlgorithms
            + ", tokenValidityDays="
            + this.tokenValidityDays
            + ")";
      }
   }
}
