package com.airlock.iam.admin.application.configuration.generic;

import com.airlock.iam.admin.api.domain.model.user.AuthenticationMethods;
import com.airlock.iam.admin.api.domain.service.user.ManagedUserRepository;
import com.airlock.iam.admin.application.configuration.generic.ui.GenericTokenControllerUiConfig;
import com.airlock.iam.admin.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.admin.domain.model.token.TokenController;
import com.airlock.iam.admin.domain.model.token.generic.GenericAuthenticationMethod;
import com.airlock.iam.admin.infrastructure.factory.TokenServiceFactory;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.EnumProperty;
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.inject.Provider;
import java.util.Optional;

@Plugin(
   description = "Generic Token Controller which allows a customized UI.\nUse this plugin if you need to manage a custom token in the Adminapp or if\nyou need other functionality than the provided Token Controllers offer.",
   displayName = "Generic Token Controller"
)
public final class GenericTokenControllerConfig implements TokenController {
   @TokenController.TokenControllerId
   @StringProperty(
      description = "<p>Unique identifier for the token controller. Serves as token type ID in the REST interface.</p>\n<p>This is also the \"auth method\" that is set on the user as active/next authentication method,\ni.e. it must match the \"Authentication Method ID\" of corresponding authentication flow steps.</p>\n<p>Finally, this ID also determines the name (label) of this token controller in the Adminapp UI,\nas defined by the resource key '<tt>user.auth-methods.type.generic.&lt;id&gt;</tt>',\nas well as the label for \"auth method\" specific lock reasons defined by the resource key\n'<tt>user.account-state.LockReason.TooManyAuthAtts.&lt;id&gt;</tt>'.</p>\nPlease note that the length of this ID must not be longer than 22 characters in order to comply with the\ndefault DB schema restrictions for column <tt>lock_reason</tt>.",
      optional = false,
      exampleValues = {"EMAILOTP", "DEVICE_TOKEN", "securid"},
      displayName = "ID",
      validationRegex = "[a-zA-Z0-9_]+"
   )
   private String id;
   @PluginProperty(
      description = "Defines the graphical UI of this token controller in the Adminapp.\nIf configured, the token controller is displayed as separate tab on the user management tab sheet using the configured ID.\nOtherwise, the token controller can only be used via the REST API.",
      displayName = "User Interface",
      optional = true
   )
   private GenericTokenControllerUiConfig ui;
   @PluginProperty(
      description = "Configures the REST endpoint for this token.\nThe configuration defines the persistency and which token data is provided for the generic REST endpoint.",
      displayName = "Token Endpoint",
      optional = false
   )
   private TokenEndpointConfig tokenEndpoint;
   public static final String TOKEN_ENDPOINT_DISPLAY_NAME = "Token Endpoint";
   @EnumProperty(
      description = "\tType of events published on changes to the token data:\n\t<ul>\n\t\t<li><b>NONE</b>: no events are published (default)</li>\n\t\t<li><b>EMAIL</b>: Events of type \"Email Address Changed\", \"Email Address Added\" and \"Email Address Deleted\" are published</li>\n\t\t<li><b>DEVICE_TOKEN</b>: Events of type \"Device Token Deleted\" are published</li>\n\t</ul>\n",
      optional = true,
      defaultValue = "NONE"
   )
   private GenericTokenControllerConfig.TokenEventType tokenEventType;
   @BooleanProperty(description = "Whether or not this token can be chosen as active authentication method.", optional = true, defaultValue = "true")
   private boolean allowedAsActiveAuthMethod;
   @BooleanProperty(description = "Whether or not this token can be chosen as new authentication method.", optional = true, defaultValue = "true")
   private boolean allowedAsNewAuthMethod;
   @BooleanProperty(description = "Whether or not this token can be chosen for an authentication method migration.", optional = true, defaultValue = "true")
   private boolean allowedForAuthMethodMigration;
   @BooleanProperty(
      description = "If enabled, an activation order is automatically placed when this authentication method is added to a user.",
      optional = true,
      defaultValue = "false"
   )
   private boolean autoOrderOnAuthMethodAdd;
   @BooleanProperty(description = "If enabled, an activation order is automatically placed when the user is created.", optional = true, defaultValue = "false")
   private boolean autoOrderOnUserCreate;

   @Override
   public AuthenticationMethods.AuthenticationMethodId getConfiguredId() {
      return AuthenticationMethods.AuthenticationMethodId.authMethodId(this.id);
   }

   @Override
   public String getTokenControllerId() {
      return this.id;
   }

   public Optional<GenericTokenControllerUiConfig> genericTokenControllerUi() {
      return Optional.ofNullable(this.ui);
   }

   public TokenEndpointConfig getTokenEndpoint() {
      return this.tokenEndpoint;
   }

   public GenericTokenControllerConfig.TokenEventType getTokenEventType() {
      return this.tokenEventType;
   }

   public AuthenticationMethod authenticationMethod(ManagedUserRepository userRepository, Provider<TokenServiceFactory> tokenServiceFactory) {
      return GenericAuthenticationMethod.builder()
         .tokenType(AuthenticationMethod.TokenType.tokenType(this.id))
         .userRepository(userRepository)
         .tokenServiceFactory(tokenServiceFactory)
         .allowedAsActive(this.allowedAsActiveAuthMethod)
         .allowedAsNew(this.allowedAsNewAuthMethod)
         .allowedForMigration(this.allowedForAuthMethodMigration)
         .autoOrderOnAuthMethodAdd(this.autoOrderOnAuthMethodAdd)
         .autoOrderOnUserCreate(this.autoOrderOnUserCreate)
         .hasGenericUi(this.ui != null)
         .build();
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public GenericTokenControllerConfig(
      String id,
      GenericTokenControllerUiConfig ui,
      TokenEndpointConfig tokenEndpoint,
      GenericTokenControllerConfig.TokenEventType tokenEventType,
      boolean allowedAsActiveAuthMethod,
      boolean allowedAsNewAuthMethod,
      boolean allowedForAuthMethodMigration,
      boolean autoOrderOnAuthMethodAdd,
      boolean autoOrderOnUserCreate
   ) {
      this.id = id;
      this.ui = ui;
      this.tokenEndpoint = tokenEndpoint;
      this.tokenEventType = tokenEventType;
      this.allowedAsActiveAuthMethod = allowedAsActiveAuthMethod;
      this.allowedAsNewAuthMethod = allowedAsNewAuthMethod;
      this.allowedForAuthMethodMigration = allowedForAuthMethodMigration;
      this.autoOrderOnAuthMethodAdd = autoOrderOnAuthMethodAdd;
      this.autoOrderOnUserCreate = autoOrderOnUserCreate;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class GenericTokenControllerConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String id;
      @SuppressFBWarnings(justification = "generated code")
      private GenericTokenControllerUiConfig ui;
      @SuppressFBWarnings(justification = "generated code")
      private TokenEndpointConfig tokenEndpoint;
      @SuppressFBWarnings(justification = "generated code")
      private GenericTokenControllerConfig.TokenEventType tokenEventType;
      @SuppressFBWarnings(justification = "generated code")
      private boolean allowedAsActiveAuthMethod;
      @SuppressFBWarnings(justification = "generated code")
      private boolean allowedAsNewAuthMethod;
      @SuppressFBWarnings(justification = "generated code")
      private boolean allowedForAuthMethodMigration;
      @SuppressFBWarnings(justification = "generated code")
      private boolean autoOrderOnAuthMethodAdd;
      @SuppressFBWarnings(justification = "generated code")
      private boolean autoOrderOnUserCreate;

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

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder id(String id) {
         this.id = id;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder ui(GenericTokenControllerUiConfig ui) {
         this.ui = ui;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder tokenEndpoint(TokenEndpointConfig tokenEndpoint) {
         this.tokenEndpoint = tokenEndpoint;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder tokenEventType(GenericTokenControllerConfig.TokenEventType tokenEventType) {
         this.tokenEventType = tokenEventType;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder allowedAsActiveAuthMethod(boolean allowedAsActiveAuthMethod) {
         this.allowedAsActiveAuthMethod = allowedAsActiveAuthMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder allowedAsNewAuthMethod(boolean allowedAsNewAuthMethod) {
         this.allowedAsNewAuthMethod = allowedAsNewAuthMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder allowedForAuthMethodMigration(boolean allowedForAuthMethodMigration) {
         this.allowedForAuthMethodMigration = allowedForAuthMethodMigration;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder autoOrderOnAuthMethodAdd(boolean autoOrderOnAuthMethodAdd) {
         this.autoOrderOnAuthMethodAdd = autoOrderOnAuthMethodAdd;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig.GenericTokenControllerConfigBuilder autoOrderOnUserCreate(boolean autoOrderOnUserCreate) {
         this.autoOrderOnUserCreate = autoOrderOnUserCreate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public GenericTokenControllerConfig build() {
         return new GenericTokenControllerConfig(
            this.id,
            this.ui,
            this.tokenEndpoint,
            this.tokenEventType,
            this.allowedAsActiveAuthMethod,
            this.allowedAsNewAuthMethod,
            this.allowedForAuthMethodMigration,
            this.autoOrderOnAuthMethodAdd,
            this.autoOrderOnUserCreate
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "GenericTokenControllerConfig.GenericTokenControllerConfigBuilder(id="
            + this.id
            + ", ui="
            + this.ui
            + ", tokenEndpoint="
            + this.tokenEndpoint
            + ", tokenEventType="
            + this.tokenEventType
            + ", allowedAsActiveAuthMethod="
            + this.allowedAsActiveAuthMethod
            + ", allowedAsNewAuthMethod="
            + this.allowedAsNewAuthMethod
            + ", allowedForAuthMethodMigration="
            + this.allowedForAuthMethodMigration
            + ", autoOrderOnAuthMethodAdd="
            + this.autoOrderOnAuthMethodAdd
            + ", autoOrderOnUserCreate="
            + this.autoOrderOnUserCreate
            + ")";
      }
   }

   public enum TokenEventType {
      NONE,
      EMAIL,
      DEVICE_TOKEN;
   }
}
