package com.airlock.iam.admin.domain.model.certificate;

import com.airlock.iam.base.api.domain.model.Entity;
import com.airlock.iam.base.api.domain.model.identity.Identity;
import com.airlock.iam.core.misc.util.crypto.CertUtils;
import com.airlock.iam.security.certificate.CertificateIO;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;

public class Certificate implements Entity<Certificate> {
   private final Identity<Certificate> id;
   private final Optional<String> serialNumber;
   private final Optional<String> issuer;
   private final Optional<String> subject;
   private final List<String> subjectAlternativeNames;
   private final Optional<Date> importedAt;
   private Optional<X509Certificate> certificate;

   public Certificate(X509Certificate certificate) {
      this(
         null,
         certificate.getSerialNumber().toString(),
         certificate.getIssuerDN().getName(),
         certificate.getSubjectDN().getName(),
         CertUtils.getAlternativeSubjectNames(certificate),
         new Date(),
         certificate
      );
   }

   public Certificate(
      Identity<Certificate> id,
      String serialNumber,
      String issuer,
      String subject,
      List<String> subjectAlternativeNames,
      Date importedAt,
      X509Certificate certificate
   ) {
      this.id = id;
      this.serialNumber = Optional.ofNullable(serialNumber);
      this.issuer = Optional.ofNullable(issuer);
      this.subject = Optional.ofNullable(subject);
      this.subjectAlternativeNames = subjectAlternativeNames != null ? subjectAlternativeNames : Collections.emptyList();
      this.importedAt = Optional.ofNullable(importedAt);
      this.certificate = Optional.ofNullable(certificate);
   }

   public Identity<Certificate> getId() {
      return this.id;
   }

   public Optional<String> getPemEncoded() {
      return this.certificate.isPresent() ? CertificateIO.asPemEncodedString(this.certificate.get()) : Optional.empty();
   }

   public CertificateValidity getValidity() {
      Optional<Date> notBefore = this.certificate.map(X509Certificate::getNotBefore);
      Optional<Date> notAfter = this.certificate.map(X509Certificate::getNotAfter);
      return new CertificateValidity(notBefore, notAfter);
   }

   public void setX509Certificate(X509Certificate x509Certificate) {
      this.certificate = Optional.ofNullable(x509Certificate);
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public Optional<String> getSerialNumber() {
      return this.serialNumber;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<String> getIssuer() {
      return this.issuer;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<String> getSubject() {
      return this.subject;
   }

   @SuppressFBWarnings(justification = "generated code")
   public List<String> getSubjectAlternativeNames() {
      return this.subjectAlternativeNames;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Date> getImportedAt() {
      return this.importedAt;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<X509Certificate> getCertificate() {
      return this.certificate;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class CertificateBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Identity<Certificate> id;
      @SuppressFBWarnings(justification = "generated code")
      private String serialNumber;
      @SuppressFBWarnings(justification = "generated code")
      private String issuer;
      @SuppressFBWarnings(justification = "generated code")
      private String subject;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> subjectAlternativeNames;
      @SuppressFBWarnings(justification = "generated code")
      private Date importedAt;
      @SuppressFBWarnings(justification = "generated code")
      private X509Certificate certificate;

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

      @SuppressFBWarnings(justification = "generated code")
      public Certificate.CertificateBuilder id(Identity<Certificate> id) {
         this.id = id;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Certificate.CertificateBuilder serialNumber(String serialNumber) {
         this.serialNumber = serialNumber;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Certificate.CertificateBuilder issuer(String issuer) {
         this.issuer = issuer;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Certificate.CertificateBuilder subject(String subject) {
         this.subject = subject;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Certificate.CertificateBuilder subjectAlternativeNames(List<String> subjectAlternativeNames) {
         this.subjectAlternativeNames = subjectAlternativeNames;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Certificate.CertificateBuilder importedAt(Date importedAt) {
         this.importedAt = importedAt;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Certificate.CertificateBuilder certificate(X509Certificate certificate) {
         this.certificate = certificate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Certificate build() {
         return new Certificate(this.id, this.serialNumber, this.issuer, this.subject, this.subjectAlternativeNames, this.importedAt, this.certificate);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Certificate.CertificateBuilder(id="
            + this.id
            + ", serialNumber="
            + this.serialNumber
            + ", issuer="
            + this.issuer
            + ", subject="
            + this.subject
            + ", subjectAlternativeNames="
            + this.subjectAlternativeNames
            + ", importedAt="
            + this.importedAt
            + ", certificate="
            + this.certificate
            + ")";
      }
   }
}
