package com.airlock.iam.oauth2.application.configuration;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.core.domain.model.http.HttpRequestHeader;
import com.airlock.iam.core.domain.model.http.HttpRequestParameter;
import com.airlock.iam.oauth2.application.configuration.clientauthentication.OAuth2BasicAuthClientSecretConfig;
import com.airlock.iam.oauth2.application.configuration.clientauthentication.OAuth2ParameterClientSecretConfig;
import com.airlock.iam.oauth2.domain.model.client.OAuth2ClientSideCredentials;
import com.airlock.iam.oauth2.domain.model.client.communication.OAuth2TokenRequestClientSecretProcessor;
import com.airlock.iam.plugin.framework.application.service.builder.plugin.PluginPropertyBuilderInitializer;
import com.google.common.base.Suppliers;
import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.Supplier;

public final class OpenIDConnectDiscoveryClientSecretConfig implements OAuth2TokenRequestClientSecretProcessor {
   private static final String CLIENT_ID_PARAMETER = "client_id";
   private static final String CLIENT_SECRET_PARAMETER = "client_secret";
   private final Supplier<OAuth2TokenRequestClientSecretProcessor> clientSecretConfig;

   public OpenIDConnectDiscoveryClientSecretConfig(OIDCProviderMetadata metadata) {
      this.clientSecretConfig = Suppliers.memoize(() -> compatibleClientSecretConfig(metadata));
   }

   @Override
   public void putClientSecretInRequestData(
      URI baseUri, List<HttpRequestParameter> params, List<HttpRequestHeader> headers, OAuth2ClientSideCredentials clientCredentials
   ) {
      this.config().putClientSecretInRequestData(baseUri, params, headers, clientCredentials);
   }

   @Override
   public com.airlock.iam.oauth2.api.domain.model.clientauthentication.ClientAuthenticationMethod clientAuthenticationMethod() {
      return this.config().clientAuthenticationMethod();
   }

   private OAuth2TokenRequestClientSecretProcessor config() {
      return this.clientSecretConfig.get();
   }

   private static OAuth2TokenRequestClientSecretProcessor compatibleClientSecretConfig(OIDCProviderMetadata metadata) {
      List<ClientAuthenticationMethod> tokenEndpointAuthMethods = metadata.getTokenEndpointAuthMethods();
      if (tokenEndpointAuthMethods.contains(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) {
         return basicAuthClientSecretConfig();
      } else if (tokenEndpointAuthMethods.contains(ClientAuthenticationMethod.CLIENT_SECRET_POST)) {
         return postParameterClientSecretConfig();
      } else {
         throw new FailureException("No compatible client authentication method found.");
      }
   }

   private static OAuth2TokenRequestClientSecretProcessor postParameterClientSecretConfig() {
      return ((OAuth2ParameterClientSecretConfig.OAuth2ParameterClientSecretConfigBuilder)PluginPropertyBuilderInitializer.pluginBuilder(
            OAuth2ParameterClientSecretConfig.OAuth2ParameterClientSecretConfigBuilder.class
         ))
         .clientIdName("client_id")
         .parameterName("client_secret")
         .build();
   }

   private static OAuth2BasicAuthClientSecretConfig basicAuthClientSecretConfig() {
      return ((OAuth2BasicAuthClientSecretConfig.OAuth2BasicAuthClientSecretConfigBuilder)PluginPropertyBuilderInitializer.pluginBuilder(
            OAuth2BasicAuthClientSecretConfig.OAuth2BasicAuthClientSecretConfigBuilder.class
         ))
         .charset(StandardCharsets.UTF_8.name())
         .build();
   }
}
