package com.airlock.iam.common.api.domain.model.certificate;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Base64;
import java.util.Base64.Encoder;
import lombok.NonNull;

public final class ClientCertificate {
   @NonNull
   private final X509Certificate certificate;

   public X509Certificate getX509Certificate() {
      return this.certificate;
   }

   public CertificateX500Name getIssuer() {
      return new CertificateX500Name(this.certificate.getIssuerX500Principal());
   }

   public CertificateX500Name getSubject() {
      return new CertificateX500Name(this.certificate.getSubjectX500Principal());
   }

   public String getSerial() {
      return this.certificate.getSerialNumber().toString();
   }

   public Instant getNotBefore() {
      return this.certificate.getNotBefore().toInstant();
   }

   public Instant getNotAfter() {
      return this.certificate.getNotAfter().toInstant();
   }

   public String getFingerprint() {
      return this.getFingerprint(ClientCertificate.FingerprintAlgorithm.SHA_256, Base64.getEncoder());
   }

   public String getFingerprint(ClientCertificate.FingerprintAlgorithm algorithm, Encoder encoder) {
      try {
         MessageDigest digest = MessageDigest.getInstance(algorithm.digestName);
         digest.update(this.certificate.getEncoded());
         return encoder.encodeToString(digest.digest());
      } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
         throw new FailureException(e);
      }
   }

   public static ClientCertificate of(X509Certificate certificate) {
      return new ClientCertificate(certificate);
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public ClientCertificate(@NonNull X509Certificate certificate) {
      if (certificate == null) {
         throw new NullPointerException("certificate is marked non-null but is null");
      }

      this.certificate = certificate;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof ClientCertificate other)) {
         return false;
      } else {
         Object this$certificate = this.certificate;
         Object other$certificate = other.certificate;
         return this$certificate == null ? other$certificate == null : this$certificate.equals(other$certificate);
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public int hashCode() {
      int PRIME = 59;
      int result = 1;
      Object $certificate = this.certificate;
      return result * 59 + ($certificate == null ? 43 : $certificate.hashCode());
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class ClientCertificateBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private X509Certificate certificate;

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

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificate.ClientCertificateBuilder certificate(@NonNull X509Certificate certificate) {
         if (certificate == null) {
            throw new NullPointerException("certificate is marked non-null but is null");
         }

         this.certificate = certificate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ClientCertificate build() {
         return new ClientCertificate(this.certificate);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "ClientCertificate.ClientCertificateBuilder(certificate=" + this.certificate + ")";
      }
   }

   public enum FingerprintAlgorithm {
      SHA_1("SHA-1"),
      SHA_256("SHA-256"),
      SHA_384("SHA-384"),
      SHA_512("SHA-512");

      private final String digestName;

      @SuppressFBWarnings(justification = "generated code")
      FingerprintAlgorithm(final String digestName) {
         this.digestName = digestName;
      }

      @SuppressFBWarnings(justification = "generated code")
      public String getDigestName() {
         return this.digestName;
      }
   }
}
