package com.airlock.iam.oauth2.domain.service.token;

import com.airlock.iam.common.api.domain.model.certificate.ClientCertificate;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.oauth2.api.domain.model.clientauthentication.OAuth2ClientAuthentication;
import com.airlock.iam.oauth2.domain.model.clientauthentication.OAuth2ClientCertificateAuthenticationPayload;
import com.airlock.iam.oauth2.domain.model.token.OAuth2ClientCertificateBindingConfirmationMethod;
import com.airlock.iam.oauth2.domain.model.token.OAuth2TokenBinding;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Optional;
import lombok.NonNull;

public final class OAuth2ClientCertificateTokenBinder implements OAuth2TokenBinder {
   @NonNull
   private final OAuth2ClientCertificateBindingConfirmationMethod confirmationMethod;
   @NonNull
   private final ELogger logger;

   @Override
   public Optional<OAuth2TokenBinding> bind(OAuth2ClientAuthentication clientAuthentication) {
      return Optional.ofNullable(clientAuthentication)
         .map(OAuth2ClientAuthentication::getPayload)
         .filter(OAuth2ClientCertificateAuthenticationPayload.class::isInstance)
         .map(OAuth2ClientCertificateAuthenticationPayload.class::cast)
         .map(OAuth2ClientCertificateAuthenticationPayload::certificate)
         .map(this::createTokenBinding);
   }

   private OAuth2TokenBinding createTokenBinding(X509Certificate certificate) {
      return OAuth2TokenBinding.tokenBinding(
         this.confirmationMethod.getMethod(), new ClientCertificate(certificate).getFingerprint(this.confirmationMethod.getAlgorithm(), Base64.getUrlEncoder())
      );
   }

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

   @SuppressFBWarnings(justification = "generated code")
   private OAuth2ClientCertificateTokenBinder(@NonNull OAuth2ClientCertificateBindingConfirmationMethod confirmationMethod, @NonNull ELogger logger) {
      if (confirmationMethod == null) {
         throw new NullPointerException("confirmationMethod is marked non-null but is null");
      }

      if (logger == null) {
         throw new NullPointerException("logger is marked non-null but is null");
      }

      this.confirmationMethod = confirmationMethod;
      this.logger = logger;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2ClientCertificateTokenBinderBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private OAuth2ClientCertificateBindingConfirmationMethod confirmationMethod;
      @SuppressFBWarnings(justification = "generated code")
      private ELogger logger;

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientCertificateTokenBinder.OAuth2ClientCertificateTokenBinderBuilder confirmationMethod(
         @NonNull OAuth2ClientCertificateBindingConfirmationMethod confirmationMethod
      ) {
         if (confirmationMethod == null) {
            throw new NullPointerException("confirmationMethod is marked non-null but is null");
         }

         this.confirmationMethod = confirmationMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientCertificateTokenBinder.OAuth2ClientCertificateTokenBinderBuilder logger(@NonNull ELogger logger) {
         if (logger == null) {
            throw new NullPointerException("logger is marked non-null but is null");
         }

         this.logger = logger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientCertificateTokenBinder build() {
         return new OAuth2ClientCertificateTokenBinder(this.confirmationMethod, this.logger);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2ClientCertificateTokenBinder.OAuth2ClientCertificateTokenBinderBuilder(confirmationMethod="
            + this.confirmationMethod
            + ", logger="
            + this.logger
            + ")";
      }
   }
}
