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

import com.airlock.iam.admin.api.domain.model.user.AuthenticationMethods;
import com.airlock.iam.admin.domain.model.token.TokenController;
import com.airlock.iam.factor.application.configuration.fido.FidoAaguidMappingsConfig;
import com.airlock.iam.factor.application.configuration.fido.FidoRepositoryConfig;
import com.airlock.iam.plugin.api.annotation.config.Groups;
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.PluginListProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Plugin(displayName = "FIDO Token Controller", description = "Plugin to manage a user's FIDO credentials.")
@PluginLicenseRestriction(moduleAffiliation = "FIDO")
@Groups(groupSequence = {"Basic Settings", "Advanced Settings"}, openGroups = "Basic Settings")
public final class FidoTokenControllerConfig implements TokenController {
   @TokenController.TokenControllerId
   public static final String ID = "FIDO";
   @PluginProperty(description = "Configures the repository to load / store FIDO data.", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private FidoRepositoryConfig repository;
   @BooleanProperty(description = "Disable to prevent FIDO from being selected as the active authentication method.", optional = true, defaultValue = "true")
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean selectableAsActiveAuthMethod;
   @BooleanProperty(
      description = "Disable to prevent FIDO from being selected as the next authentication method for token migration.",
      optional = true,
      defaultValue = "true"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean selectableAsNextAuthMethod;
   @PluginListProperty(
      displayName = "FIDO AAGUID Mappings",
      description = "List of recognized FIDO authenticators. Maps a unique ID provided by a FIDO authenticator (AAGUID) to a descriptive string displayed in the FIDO token controller (Adminapp, Adminapp REST API). <p>The order is important since in case multiple FIDO authenticators are defined with the same AAGUID, only the last entry in the list will be considered. This allows to override default information provided by the plugin' FIDO Default AAGUID Mappings' when configured in the list.</p>",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private List<FidoAaguidMappingsConfig> aaguidMappings;

   @Override
   public AuthenticationMethods.AuthenticationMethodId getConfiguredId() {
      return AuthenticationMethods.AuthenticationMethodId.authMethodId("FIDO");
   }

   @Override
   public String getTokenControllerId() {
      return "FIDO";
   }

   public List<FidoAaguidMappingsConfig> getAaguidMappings() {
      return this.aaguidMappings;
   }

   public FidoRepositoryConfig getRepository() {
      return this.repository;
   }

   public boolean isSelectableAsActiveAuthMethod() {
      return this.selectableAsActiveAuthMethod;
   }

   public boolean isSelectableAsNextAuthMethod() {
      return this.selectableAsNextAuthMethod;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public FidoTokenControllerConfig(
      FidoRepositoryConfig repository, boolean selectableAsActiveAuthMethod, boolean selectableAsNextAuthMethod, List<FidoAaguidMappingsConfig> aaguidMappings
   ) {
      this.repository = repository;
      this.selectableAsActiveAuthMethod = selectableAsActiveAuthMethod;
      this.selectableAsNextAuthMethod = selectableAsNextAuthMethod;
      this.aaguidMappings = aaguidMappings;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class FidoTokenControllerConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private FidoRepositoryConfig repository;
      @SuppressFBWarnings(justification = "generated code")
      private boolean selectableAsActiveAuthMethod;
      @SuppressFBWarnings(justification = "generated code")
      private boolean selectableAsNextAuthMethod;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<FidoAaguidMappingsConfig> aaguidMappings;

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

      @SuppressFBWarnings(justification = "generated code")
      public FidoTokenControllerConfig.FidoTokenControllerConfigBuilder repository(FidoRepositoryConfig repository) {
         this.repository = repository;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoTokenControllerConfig.FidoTokenControllerConfigBuilder selectableAsActiveAuthMethod(boolean selectableAsActiveAuthMethod) {
         this.selectableAsActiveAuthMethod = selectableAsActiveAuthMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoTokenControllerConfig.FidoTokenControllerConfigBuilder selectableAsNextAuthMethod(boolean selectableAsNextAuthMethod) {
         this.selectableAsNextAuthMethod = selectableAsNextAuthMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoTokenControllerConfig.FidoTokenControllerConfigBuilder addAuthenticatorMetadata(FidoAaguidMappingsConfig addAuthenticatorMetadata) {
         if (this.aaguidMappings == null) {
            this.aaguidMappings = new ArrayList<>();
         }

         this.aaguidMappings.add(addAuthenticatorMetadata);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoTokenControllerConfig.FidoTokenControllerConfigBuilder aaguidMappings(Collection<? extends FidoAaguidMappingsConfig> aaguidMappings) {
         if (aaguidMappings == null) {
            throw new NullPointerException("aaguidMappings cannot be null");
         }

         if (this.aaguidMappings == null) {
            this.aaguidMappings = new ArrayList<>();
         }

         this.aaguidMappings.addAll(aaguidMappings);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoTokenControllerConfig.FidoTokenControllerConfigBuilder clearAaguidMappings() {
         if (this.aaguidMappings != null) {
            this.aaguidMappings.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoTokenControllerConfig build() {
         return new FidoTokenControllerConfig(
            this.repository,
            this.selectableAsActiveAuthMethod,
            this.selectableAsNextAuthMethod,
            switch (this.aaguidMappings == null ? 0 : this.aaguidMappings.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.aaguidMappings.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.aaguidMappings));
            }
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "FidoTokenControllerConfig.FidoTokenControllerConfigBuilder(repository="
            + this.repository
            + ", selectableAsActiveAuthMethod="
            + this.selectableAsActiveAuthMethod
            + ", selectableAsNextAuthMethod="
            + this.selectableAsNextAuthMethod
            + ", aaguidMappings="
            + this.aaguidMappings
            + ")";
      }
   }
}
