package com.airlock.iam.common.domain.service.jwt.signature;

import com.airlock.iam.common.domain.model.jwt.JwtClaimsSet;
import com.airlock.iam.common.domain.model.jwt.signature.JsonWebSignatureToken;
import com.airlock.iam.common.domain.model.jwt.signature.JwsAlgorithm;
import com.airlock.iam.common.domain.service.jwt.JwtSettingsValidationError;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.text.ParseException;
import java.util.Optional;

public final class JwsSignerWrapper {
   private final JWSSigner signer;

   Optional<JwtSettingsValidationError> validateAlgorithmSupported(JwsAlgorithm signatureAlgorithm) {
      return !this.isAlgorithmSupported(signatureAlgorithm)
         ? Optional.of(
            new JwtSettingsValidationError("The selected algorithm (" + signatureAlgorithm.name() + ") is not supported by the given key", Optional.empty())
         )
         : Optional.empty();
   }

   private boolean isAlgorithmSupported(JwsAlgorithm jwsAlgorithm) {
      return this.signer.supportedJWSAlgorithms().contains(JWSAlgorithm.parse(jwsAlgorithm.name()));
   }

   private JWTClaimsSet parseClaimsSet(JwtClaimsSet payload) throws JwtSignerException {
      try {
         return JWTClaimsSet.parse(payload.toJsonString());
      } catch (ParseException e) {
         throw new JwtSignerException("Could not parse claimsSet", e);
      }
   }

   public JsonWebSignatureToken sign(JWSHeader header, JwtClaimsSet claimsSet) throws JwtSignerException {
      SignedJWT signedJwt = new SignedJWT(header, this.parseClaimsSet(claimsSet));

      try {
         signedJwt.sign(this.signer);
         return JsonWebSignatureToken.of(signedJwt.serialize());
      } catch (JOSEException e) {
         throw new JwtSignerException("Error signing JWT: " + e.getMessage(), e);
      }
   }

   public static JwsSignerWrapper signerWrapper(JWSSigner signer) {
      return new JwsSignerWrapper(signer);
   }

   @SuppressFBWarnings(justification = "generated code")
   public JwsSignerWrapper(JWSSigner signer) {
      this.signer = signer;
   }
}
