package com.airlock.iam.common.application.configuration.certificate;

import com.airlock.iam.common.api.application.configuration.credential.RequestAuthenticationConfigForAdminapp;
import com.airlock.iam.common.api.application.configuration.credential.RequestAuthenticationConfigForLoginapp;
import com.airlock.iam.common.api.application.configuration.credential.RequestAuthenticationConfigForTransactionApproval;
import com.airlock.iam.common.application.configuration.credential.BaseRequestAuthenticationConfig;
import com.airlock.iam.core.application.configuration.store.user.UserStoreProvider;
import com.airlock.iam.core.misc.authen.UsernameTransformer;
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.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.StringProperty;
import com.airlock.iam.security.certificate.CertificateStatusChecker;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Plugin(
   description = "<p>Authenticates single requests by their client certificate.</p>\n<p><i>Warning 1:</i> This authentication assumes that some external process\ncan guarantee that the certificate belongs to the authenticating entity. This\nis typically done by challenging the entity to sign something with the\ncorresponding private key. This is, for example, the case in an SSL handshake involving client certificate verification.</p>\n<p><i>Warning 2:</i> This authentication does <b>not check</b> whether the\ncertificate was signed by a trusted entity. This must be done during the SSL handshake.</p>",
   displayName = "Client Certificate (X.509) Request Authentication"
)
@Groups(groupSequence = {"Basic Settings", "Resolve User Settings", "Advanced Settings"}, openGroups = "Basic Settings")
public final class ClientCertificateRequestAuthenticationConfig
   extends BaseRequestAuthenticationConfig
   implements RequestAuthenticationConfigForAdminapp,
   RequestAuthenticationConfigForLoginapp,
   RequestAuthenticationConfigForTransactionApproval {
   static final String RESOLVE_USER = "Resolve User Settings";
   @StringProperty(
      description = "<p>Defines how the username is extracted from the certificate.</p>\n<p>Usually the username is part of the DN (distinguished name) of the certified subject.\nThis attribute specifies the attribute name of the username in the DN. Example:\nThe value \"cn\" will extract the common name from the DN and use this as username.</p>\n<p>The following values are interpreted separately:\n<ul>\n<li><b>dn</b>: the whole distinguished name is used.</li>\n<li><b>subjectAlternativeName</b>: the alternative subject name is used.</li>\n<li><b>certificate</b>: the base64 encoded certificate.</li>\n</ul></p>\n<p>Username transformation can be used to lookup the user based on a context-data field or to\nmodify the extracted username (e.g. to strip the domain from the name).</p>",
      suggestedValues = {"cn", "sAMAccountName", "dn", "subjectAlternativeName", "certificate"},
      optional = false
   )
   @GroupProperty(groupIdentifier = "Resolve User Settings")
   private String userAttribute;
   @BooleanProperty(
      description = "If enabled, the validity period of the certificate is checked. If disabled, expired (or not yet valid) certificates are also accepted.",
      optional = true,
      defaultValue = "true"
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean checkValidityPeriod;
   @PluginListProperty(
      description = "A list of certificate status checkers used to check the revocation status of the client certificate. If more than one checker is configured, all of them are consulted and the certificate is considered revoked if at least one of them tells so.",
      displayName = "Certificate Status Checkers",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private List<CertificateStatusChecker> certStatusCheckers;

   private ClientCertificateRequestAuthenticationConfig(
      UserStoreProvider userStore,
      List<UsernameTransformer> usernameTransformers,
      List<String> staticRoles,
      List<String> rolesBlocklist,
      String userAttribute,
      boolean checkValidityPeriod,
      List<CertificateStatusChecker> certStatusCheckers
   ) {
      super(userStore, usernameTransformers, staticRoles, rolesBlocklist);
      this.userAttribute = userAttribute;
      this.checkValidityPeriod = checkValidityPeriod;
      this.certStatusCheckers = certStatusCheckers;
   }

   public String getUserAttribute() {
      return this.userAttribute;
   }

   public boolean isCheckValidityPeriod() {
      return this.checkValidityPeriod;
   }

   public List<CertificateStatusChecker> getCertStatusCheckers() {
      return this.certStatusCheckers;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class ClientCertificateRequestAuthenticationConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserStoreProvider userStore;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<UsernameTransformer> usernameTransformers;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> staticRoles;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> rolesBlocklist;
      @SuppressFBWarnings(justification = "generated code")
      private String userAttribute;
      @SuppressFBWarnings(justification = "generated code")
      private boolean checkValidityPeriod;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<CertificateStatusChecker> certStatusCheckers;

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

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder userStore(UserStoreProvider userStore) {
         this.userStore = userStore;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder usernameTransformer(
         UsernameTransformer usernameTransformer
      ) {
         if (this.usernameTransformers == null) {
            this.usernameTransformers = new ArrayList<>();
         }

         this.usernameTransformers.add(usernameTransformer);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder usernameTransformers(
         Collection<? extends UsernameTransformer> usernameTransformers
      ) {
         if (usernameTransformers == null) {
            throw new NullPointerException("usernameTransformers cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder clearUsernameTransformers() {
         if (this.usernameTransformers != null) {
            this.usernameTransformers.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder staticRole(String staticRole) {
         if (this.staticRoles == null) {
            this.staticRoles = new ArrayList<>();
         }

         this.staticRoles.add(staticRole);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder staticRoles(
         Collection<? extends String> staticRoles
      ) {
         if (staticRoles == null) {
            throw new NullPointerException("staticRoles cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder clearStaticRoles() {
         if (this.staticRoles != null) {
            this.staticRoles.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder roleForBlocklist(String roleForBlocklist) {
         if (this.rolesBlocklist == null) {
            this.rolesBlocklist = new ArrayList<>();
         }

         this.rolesBlocklist.add(roleForBlocklist);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder rolesBlocklist(
         Collection<? extends String> rolesBlocklist
      ) {
         if (rolesBlocklist == null) {
            throw new NullPointerException("rolesBlocklist cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder clearRolesBlocklist() {
         if (this.rolesBlocklist != null) {
            this.rolesBlocklist.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder userAttribute(String userAttribute) {
         this.userAttribute = userAttribute;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder checkValidityPeriod(boolean checkValidityPeriod) {
         this.checkValidityPeriod = checkValidityPeriod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder certStatusChecker(
         CertificateStatusChecker certStatusChecker
      ) {
         if (this.certStatusCheckers == null) {
            this.certStatusCheckers = new ArrayList<>();
         }

         this.certStatusCheckers.add(certStatusChecker);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder certStatusCheckers(
         Collection<? extends CertificateStatusChecker> certStatusCheckers
      ) {
         if (certStatusCheckers == null) {
            throw new NullPointerException("certStatusCheckers cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder clearCertStatusCheckers() {
         if (this.certStatusCheckers != null) {
            this.certStatusCheckers.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificateRequestAuthenticationConfig build() {
         List usernameTransformers = switch (this.usernameTransformers == null ? 0 : this.usernameTransformers.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.usernameTransformers.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.usernameTransformers));
         };

         List staticRoles = switch (this.staticRoles == null ? 0 : this.staticRoles.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.staticRoles.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.staticRoles));
         };

         List rolesBlocklist = switch (this.rolesBlocklist == null ? 0 : this.rolesBlocklist.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.rolesBlocklist.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.rolesBlocklist));
         };

         return new ClientCertificateRequestAuthenticationConfig(
            this.userStore,
            usernameTransformers,
            staticRoles,
            rolesBlocklist,
            this.userAttribute,
            this.checkValidityPeriod,
            switch (this.certStatusCheckers == null ? 0 : this.certStatusCheckers.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.certStatusCheckers.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.certStatusCheckers));
            }
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "ClientCertificateRequestAuthenticationConfig.ClientCertificateRequestAuthenticationConfigBuilder(userStore="
            + this.userStore
            + ", usernameTransformers="
            + this.usernameTransformers
            + ", staticRoles="
            + this.staticRoles
            + ", rolesBlocklist="
            + this.rolesBlocklist
            + ", userAttribute="
            + this.userAttribute
            + ", checkValidityPeriod="
            + this.checkValidityPeriod
            + ", certStatusCheckers="
            + this.certStatusCheckers
            + ")";
      }
   }
}
