package com.airlock.iam.techclientreg.application.configuration.oauth2dcr;

import com.airlock.iam.core.misc.authen.SecretStringGenerator;
import com.airlock.iam.oauth2.application.configuration.OAuth2ClientRegistrationConfig;
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 com.airlock.iam.techclientreg.api.application.configuration.oauth2.OAuth2ClientMetadataProcessorConfig;
import com.airlock.iam.techclientreg.application.configuration.registration.clientid.DcrClientIdGeneratorConfig;
import com.airlock.iam.techclientreg.application.configuration.registration.clientid.UuidClientIdConfig;
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>Service for OAuth 2.0 Dynamic Client Registration Protocol that can be used\nfor the most common use cases.</p>\n<p>Dynamic Client Registration uses the following endpoint:\n<code>/&lt;loginapp-uri&gt;/rest/public/tech-client-registration/oauth2/<i>&lt;as-identifier&gt;</i>/register</code>\n</p>\n",
   displayName = "OAuth 2.0 Dynamic Client Registration"
)
@PluginLicenseRestriction(moduleAffiliation = "TechClientRegistration")
@Groups(groupSequence = {"Basic Settings", "Supported Grants"}, openGroups = {"Basic Settings", "Supported Grants"})
public class DefaultOAuth2ClientRegistrationConfig implements OAuth2ClientRegistrationConfig {
   static final String GRANTS_GROUP = "Supported Grants";
   @PluginProperty(
      description = "Defines how the client_id is determined. It can either be generated or taken from a request parameter.",
      displayName = "Client ID",
      defaultValue = UuidClientIdConfig.class,
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private DcrClientIdGeneratorConfig clientIdGenerator;
   @PluginProperty(
      description = "Generator for the client_secret. The client secret is only generated, if this property is configured and the determined token_endpoint_auth_method is either \"client_secret_basic\" or \"client_secret_post\".<br>In addition, a \"Token Endpoint Auth Method Processor\" is required to determine the authentication method.",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private SecretStringGenerator clientSecretGenerator;
   @PluginListProperty(
      description = "Processors that handle the attributes of the registration request (other than the requested grants). Only attributes for which a processor is configured here are handled; all other aspects of the registration request are ignored.",
      displayName = "Attribute Processors",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private List<OAuth2ClientMetadataProcessorConfig> additionalProcessors;
   @BooleanProperty(
      description = "<p>If enabled, the technical client ID is included in the registration response.</p><p>The technical client ID is not identical to the client ID that is also sent with the response. The technical client ID can be used to further administer the newly created client, e.g. via Adminapp REST API.</p>",
      defaultValue = "false",
      displayName = "Return Technical Client ID",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Basic Settings")
   private boolean returnTechClientId;
   @BooleanProperty(
      description = "<p>If enabled, <em>Authorization Code Grant</em> is supported.</p><p>The registered client will be enabled to perform the authorization code grant in any of the following cases:<ul><li>If the \"grant_types\" requested by the client include \"authorization_code\". In this case a \"redirect_uri\" must be present.</li><li>If the requested \"response_types\" include \"code\". In this case a \"redirect_uri\" must be present.</li><li>If the client requests no \"grant_types\" and no \"response_types\", but supplies a \"redirect_uri\".</li></ul>The \"grant_types\" of the registered client will include \"authorization_code\" and the \"response_types\" will include \"code\".</p><p>If this property is disabled, authorization code grant is never enabled for the client.</p>",
      defaultValue = "true",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Supported Grants")
   private boolean authorizationCodeGrant;
   @BooleanProperty(
      description = "<p>If enabled, <em>Implicit Grant</em> is supported.</p><p>The registered client will be enabled to perform implicit grant in any of the following cases:<ul><li>If the \"grant_types\" requested by the client include \"implicit\". In this case, a \"redirect_uri\" must be present.</li><li>If the requested \"response_types\" include \"token\". In this case, a \"redirect_uri\" must also be present.</li><li>If the client requests no \"grant_types\" and no \"response_types\", but supplies a \"redirect_uri\".</li></ul>The \"grant_types\" of the registered client will include \"implicit\" and the \"response_types\" will include \"token\".</p><p>If this property is disabled, implicit grant is never enabled for the client.</p>",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Supported Grants")
   private boolean implicitGrant;
   @BooleanProperty(
      description = "<p>If enabled, <em>Client Credentials Grant</em> is supported.</p><p>The registered client will be enabled to perform client credentials grant in any of the following cases:<ul><li>If the \"grant_types\" requested by the client include \"client_credentials\".</li><li>If the client specifies no \"grant_types\" in the registration request.</li></ul></p><p>If this property is disabled, client credentials grant is never enabled for the client.</p>",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Supported Grants")
   private boolean clientCredentialsGrant;
   @BooleanProperty(
      description = "<p>If enabled, refreshing an access token is supported.</p><p>The registered client will be enabled to refresh access tokens in any of the following cases:<ul><li>If the \"grant_types\" requested by the client include \"refresh_token\".</li><li>If the client specifies no \"grant_types\" in the registration request.</li></ul></p><p>If this property is disabled, refreshing of access tokens is never enabled for the client.</p>",
      defaultValue = "true",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Supported Grants")
   private boolean accessTokenRefresh;

   public DcrClientIdGeneratorConfig getClientIdGenerator() {
      return this.clientIdGenerator;
   }

   public SecretStringGenerator getClientSecretGenerator() {
      return this.clientSecretGenerator;
   }

   public List<OAuth2ClientMetadataProcessorConfig> getAdditionalProcessors() {
      return this.additionalProcessors;
   }

   public boolean isAuthorizationCodeGrant() {
      return this.authorizationCodeGrant;
   }

   public boolean isImplicitGrant() {
      return this.implicitGrant;
   }

   public boolean isClientCredentialsGrant() {
      return this.clientCredentialsGrant;
   }

   public boolean isAccessTokenRefresh() {
      return this.accessTokenRefresh;
   }

   public boolean returnTechClientId() {
      return this.returnTechClientId;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public DefaultOAuth2ClientRegistrationConfig(
      DcrClientIdGeneratorConfig clientIdGenerator,
      SecretStringGenerator clientSecretGenerator,
      List<OAuth2ClientMetadataProcessorConfig> additionalProcessors,
      boolean returnTechClientId,
      boolean authorizationCodeGrant,
      boolean implicitGrant,
      boolean clientCredentialsGrant,
      boolean accessTokenRefresh
   ) {
      this.clientIdGenerator = clientIdGenerator;
      this.clientSecretGenerator = clientSecretGenerator;
      this.additionalProcessors = additionalProcessors;
      this.returnTechClientId = returnTechClientId;
      this.authorizationCodeGrant = authorizationCodeGrant;
      this.implicitGrant = implicitGrant;
      this.clientCredentialsGrant = clientCredentialsGrant;
      this.accessTokenRefresh = accessTokenRefresh;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class DefaultOAuth2ClientRegistrationConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private DcrClientIdGeneratorConfig clientIdGenerator;
      @SuppressFBWarnings(justification = "generated code")
      private SecretStringGenerator clientSecretGenerator;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<OAuth2ClientMetadataProcessorConfig> additionalProcessors;
      @SuppressFBWarnings(justification = "generated code")
      private boolean returnTechClientId;
      @SuppressFBWarnings(justification = "generated code")
      private boolean authorizationCodeGrant;
      @SuppressFBWarnings(justification = "generated code")
      private boolean implicitGrant;
      @SuppressFBWarnings(justification = "generated code")
      private boolean clientCredentialsGrant;
      @SuppressFBWarnings(justification = "generated code")
      private boolean accessTokenRefresh;

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

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder clientIdGenerator(DcrClientIdGeneratorConfig clientIdGenerator) {
         this.clientIdGenerator = clientIdGenerator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder clientSecretGenerator(
         SecretStringGenerator clientSecretGenerator
      ) {
         this.clientSecretGenerator = clientSecretGenerator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder additionalProcessor(
         OAuth2ClientMetadataProcessorConfig additionalProcessor
      ) {
         if (this.additionalProcessors == null) {
            this.additionalProcessors = new ArrayList<>();
         }

         this.additionalProcessors.add(additionalProcessor);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder additionalProcessors(
         Collection<? extends OAuth2ClientMetadataProcessorConfig> additionalProcessors
      ) {
         if (additionalProcessors == null) {
            throw new NullPointerException("additionalProcessors cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder clearAdditionalProcessors() {
         if (this.additionalProcessors != null) {
            this.additionalProcessors.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder returnTechClientId(boolean returnTechClientId) {
         this.returnTechClientId = returnTechClientId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder authorizationCodeGrant(boolean authorizationCodeGrant) {
         this.authorizationCodeGrant = authorizationCodeGrant;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder implicitGrant(boolean implicitGrant) {
         this.implicitGrant = implicitGrant;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder clientCredentialsGrant(boolean clientCredentialsGrant) {
         this.clientCredentialsGrant = clientCredentialsGrant;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder accessTokenRefresh(boolean accessTokenRefresh) {
         this.accessTokenRefresh = accessTokenRefresh;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public DefaultOAuth2ClientRegistrationConfig build() {
         return new DefaultOAuth2ClientRegistrationConfig(
            this.clientIdGenerator, this.clientSecretGenerator, switch (this.additionalProcessors == null ? 0 : this.additionalProcessors.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.additionalProcessors.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.additionalProcessors));
            }, this.returnTechClientId, this.authorizationCodeGrant, this.implicitGrant, this.clientCredentialsGrant, this.accessTokenRefresh
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "DefaultOAuth2ClientRegistrationConfig.DefaultOAuth2ClientRegistrationConfigBuilder(clientIdGenerator="
            + this.clientIdGenerator
            + ", clientSecretGenerator="
            + this.clientSecretGenerator
            + ", additionalProcessors="
            + this.additionalProcessors
            + ", returnTechClientId="
            + this.returnTechClientId
            + ", authorizationCodeGrant="
            + this.authorizationCodeGrant
            + ", implicitGrant="
            + this.implicitGrant
            + ", clientCredentialsGrant="
            + this.clientCredentialsGrant
            + ", accessTokenRefresh="
            + this.accessTokenRefresh
            + ")";
      }
   }
}
