package com.airlock.iam.admin.application.service.certificate;

import com.airlock.iam.admin.api.domain.model.administrator.CurrentAdministrator;
import com.airlock.iam.admin.domain.model.certificate.Certificate;
import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.log.api.signing.SigningLogger;
import com.airlock.iam.security.certificate.CertificateIO;
import jakarta.inject.Inject;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.List;
import java.util.Optional;

public class CertificateService {
   private static final ELogger LOG = ELoggerFactory.getLogger(CertificateService.class);
   private final CurrentAdministrator administrator;
   private final CertificateRepository repository;
   private final SigningLogger logger;

   @Inject
   public CertificateService(CurrentAdministrator administrator, CertificateRepository repository, SigningLogger logger) {
      this.administrator = administrator;
      this.repository = repository;
      this.logger = logger;
   }

   public List<Certificate> getAll(UserId userId) {
      return this.repository.getAll(userId);
   }

   public void delete(UserId userId, String certificateId) {
      this.repository.delete(userId, certificateId);
   }

   public ValidationResult validateCertificate(String pem) {
      Optional<Certificate> certificate = this.toCertificate(pem);
      return certificate.isPresent() ? ValidationResult.valid() : ValidationResult.singleError("pem", GenericError.INVALID_VALUE);
   }

   public Certificate add(UserId userId, String pem) {
      Certificate certificate = (Certificate)ViolationException.checkPresent(this.toCertificate(pem), "The provided certificate is not valid.");
      certificate = this.repository.add(userId, this.administrator.getName(), certificate);
      this.logger.log("A new certificate was added for user \"%s\"", new Object[]{userId});
      return certificate;
   }

   public Certificate update(UserId userId, String certificateId, String pem) {
      X509Certificate x509Certificate = (X509Certificate)ViolationException.checkPresent(this.toX509Certificate(pem), "The provided certificate is not valid.");
      Certificate certificate = this.repository.getById(userId, certificateId);
      certificate.setX509Certificate(x509Certificate);
      certificate = this.repository.save(certificate);
      this.logger.log("Certificate with ID \"%s\" was updated for user \"%s\"", new Object[]{certificateId, userId});
      return certificate;
   }

   private Optional<Certificate> toCertificate(String pem) {
      return this.toX509Certificate(pem).map(Certificate::new);
   }

   private Optional<X509Certificate> toX509Certificate(String pem) {
      try {
         return Optional.of(CertificateIO.certificateFromPemString(pem));
      } catch (CertificateException | ParseException e) {
         LOG.info("Error decoding certificate from PEM string: " + e.getMessage());
         return Optional.empty();
      }
   }
}
