package com.airlock.iam.oauth2.domain.model.revocation;

import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.api.domain.model.clientauthentication.ClientAuthenticationMethod;
import com.airlock.iam.oauth2.api.domain.model.clientauthentication.OAuth2AuthClientId;
import com.airlock.iam.oauth2.api.domain.model.clientauthentication.OAuth2ClientAuthenticationPayload;
import com.airlock.iam.oauth2.domain.model.clientauthentication.NoClientAuthenticationPayload;
import com.airlock.iam.oauth2.domain.model.clientauthentication.OAuth2ClientSecretAuthenticationPayload;
import com.airlock.iam.oauth2.domain.model.clientauthentication.method.KnownClientAuthenticationMethod;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.TokenRevocationRequest;
import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
import com.nimbusds.oauth2.sdk.auth.ClientSecretPost;
import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
import com.nimbusds.oauth2.sdk.http.JakartaServletUtils;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Optional;

public final class OAuth2TokenRevocationRequest {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2TokenRevocationRequest.class);
   private final TokenRevocationRequest tokenRevocationRequest;

   public static Optional<OAuth2TokenRevocationRequest> parse(HttpServletRequest request) {
      return createHTTPRequest(request).flatMap(httpRequest -> {
         try {
            return Optional.of(TokenRevocationRequest.parse(httpRequest));
         } catch (ParseException e) {
            LOG.info("Invalid OAuth 2.0 Token Revocation request: " + e.getMessage());
            return Optional.empty();
         }
      }).map(OAuth2TokenRevocationRequest::new);
   }

   private static Optional<HTTPRequest> createHTTPRequest(HttpServletRequest request) {
      try {
         return Optional.of(JakartaServletUtils.createHTTPRequest(request));
      } catch (IOException e) {
         LOG.info("Error while handling OAuth 2.0 Token Revocation request.", e);
         return Optional.empty();
      }
   }

   public String getTokenValue() {
      return this.tokenRevocationRequest.getToken().getValue();
   }

   public String getClientId() {
      ClientAuthentication clientAuthentication = this.tokenRevocationRequest.getClientAuthentication();
      return clientAuthentication != null ? clientAuthentication.getClientID().getValue() : this.tokenRevocationRequest.getClientID().getValue();
   }

   public OAuth2ClientAuthenticationPayload getAuthenticationPayload() {
      return mapClientAuthenticationPayload(this.tokenRevocationRequest.getClientAuthentication());
   }

   private static OAuth2ClientAuthenticationPayload mapClientAuthenticationPayload(ClientAuthentication clientAuthentication) {
      if (clientAuthentication instanceof ClientSecretBasic) {
         return plainClientSecretAuthentication(
            clientAuthentication, ((ClientSecretBasic)clientAuthentication).getClientSecret(), KnownClientAuthenticationMethod.BASIC_AUTH_CLIENT_SECRET
         );
      } else {
         return clientAuthentication instanceof ClientSecretPost
            ? plainClientSecretAuthentication(
               clientAuthentication, ((ClientSecretPost)clientAuthentication).getClientSecret(), KnownClientAuthenticationMethod.POST_PARAMETERS_CLIENT_SECRET
            )
            : NoClientAuthenticationPayload.NO_AUTHENTICATION_PAYLOAD;
      }
   }

   private static OAuth2ClientAuthenticationPayload plainClientSecretAuthentication(
      ClientAuthentication clientAuthentication, Secret clientSecret, ClientAuthenticationMethod method
   ) {
      return OAuth2ClientSecretAuthenticationPayload.builder()
         .clientId(OAuth2AuthClientId.oAuth2AuthClientId(clientAuthentication.getClientID().getValue()))
         .clientIdFromRequest(Optional.empty())
         .secret(clientSecret.getValue())
         .method(method)
         .build();
   }

   @SuppressFBWarnings(justification = "generated code")
   private OAuth2TokenRevocationRequest(TokenRevocationRequest tokenRevocationRequest) {
      this.tokenRevocationRequest = tokenRevocationRequest;
   }
}
