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

import com.airlock.iam.common.api.domain.model.certificate.Certificate;
import com.airlock.iam.common.api.domain.model.certificate.OrganizationIdentifier;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Optional;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;

public final class CertificateImpl implements Certificate {
   private static final ELogger LOG = ELoggerFactory.getLogger(CertificateImpl.class);
   private final X509Certificate certificate;

   @Override
   public Optional<OrganizationIdentifier> getSubjectOrganizationIdentifier() {
      return this.getSubjectName().flatMap(CertificateImpl::extractOrganizationIdentifier);
   }

   private Optional<X500Name> getSubjectName() {
      try {
         return Optional.of(new JcaX509CertificateHolder(this.certificate).getSubject());
      } catch (CertificateEncodingException e) {
         LOG.debug("The certificate X500Name could not be parsed.");
         return Optional.empty();
      }
   }

   private static Optional<OrganizationIdentifier> extractOrganizationIdentifier(X500Name subjectName) {
      RDN[] rdns = subjectName.getRDNs(X509ObjectIdentifiers.id_at_organizationIdentifier);
      if (rdns.length != 1) {
         LOG.debug("The organization identifier is either not present or there are multiple organization identifiers.");
         return Optional.empty();
      } else {
         return toSingleStringValue(rdns[0]).map(OrganizationIdentifier::new);
      }
   }

   private static Optional<String> toSingleStringValue(RDN rnd) {
      if (rnd.isMultiValued()) {
         return Optional.empty();
      }

      AttributeTypeAndValue typeAndValue = rnd.getFirst();
      if (typeAndValue == null) {
         return Optional.empty();
      }

      ASN1Encodable value = typeAndValue.getValue();
      return Optional.of(value.toString());
   }

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

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