package com.airlock.iam.oauth2.infrastructure.discovery.compatibility;

import com.airlock.iam.oauth2.api.domain.model.clientauthentication.ClientAuthenticationMethod;
import com.airlock.iam.oauth2.domain.model.clientauthentication.method.KnownClientAuthenticationMethod;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import java.util.List;
import java.util.Optional;
import java.util.Set;

public final class OpenIDConnectConfigurationTokenEndpointAuthMethods implements OpenIDConnectConfiguration {
   static final String TOKEN_ENDPOINT_AUTH_METHODS_CONFIGURATION_NAME = "token endpoint auth methods";
   private static final Set<com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod> COMPATIBLE_TOKEN_ENDPOINT_CLIENT_AUTH_METHODS = ImmutableSet.of(
      com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
      com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.CLIENT_SECRET_POST,
      com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.PRIVATE_KEY_JWT
   );
   private final Optional<ClientAuthenticationMethod> configuredAuthMethod;

   public OpenIDConnectConfigurationTokenEndpointAuthMethods(Optional<ClientAuthenticationMethod> configuredAuthMethod) {
      this.configuredAuthMethod = configuredAuthMethod;
   }

   public OpenIDConnectConfigurationTokenEndpointAuthMethods() {
      this.configuredAuthMethod = Optional.empty();
   }

   @Override
   public String getName() {
      return "token endpoint auth methods";
   }

   @Override
   public OpenIDConnectCompatibilityResult validate(OIDCProviderMetadata providerMetadata) {
      List<com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod> authMethods = providerMetadata.getTokenEndpointAuthMethods();
      Set<com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod> intersection = Sets.intersection(
         COMPATIBLE_TOKEN_ENDPOINT_CLIENT_AUTH_METHODS, Sets.newHashSet(authMethods)
      );
      if (intersection.isEmpty()) {
         return OpenIDConnectCompatibilityResult.withIncompatibility(
            new OpenIDConnectIncompatibilityNoneFoundValuesSupported<>(this, COMPATIBLE_TOKEN_ENDPOINT_CLIENT_AUTH_METHODS, authMethods)
         );
      } else {
         return this.onlyPrivateKeyJwtCompatibleButNoIamConfigIsPresent(intersection)
            ? OpenIDConnectCompatibilityResult.withIncompatibility(new OpenIDConnectIncompatibilityMissingPrivateKeyClientAuthIamConfiguration(this))
            : OpenIDConnectCompatibilityResult.SUCCESS;
      }
   }

   private boolean onlyPrivateKeyJwtCompatibleButNoIamConfigIsPresent(Set<com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod> intersection) {
      boolean onlyPrivateKeyJwtCompatible = intersection.size() == 1
         && intersection.contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.PRIVATE_KEY_JWT);
      boolean configuredAuthMethodIsNotPrivateKeyJwt = this.configuredAuthMethod.isEmpty()
         || !this.configuredAuthMethod.get().equals(KnownClientAuthenticationMethod.PRIVATE_KEY_JWT);
      return onlyPrivateKeyJwtCompatible && configuredAuthMethodIsNotPrivateKeyJwt;
   }
}
