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

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.common.domain.model.jwt.signature.JwsVerifier;
import com.airlock.iam.common.domain.model.jwt.signature.JwsVerifierImpl;
import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.ECKey;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public final class ECJsonWebKey implements JsonWebKey {
   private static final ELogger LOG = ELoggerFactory.getLogger(ECJsonWebKey.class);
   private final ECKey ecKey;

   public static ECJsonWebKey fromMap(Map<String, String> map) {
      try {
         return new ECJsonWebKey(ECKey.parse(new HashMap<>(map)));
      } catch (ParseException | IllegalArgumentException e) {
         throw new ViolationException("Cannot parse EC key: " + e.getMessage(), e);
      }
   }

   public static ECJsonWebKey fromJSON(String json) {
      try {
         return new ECJsonWebKey(ECKey.parse(json));
      } catch (ParseException | IllegalArgumentException e) {
         throw new ViolationException("Cannot parse EC key: " + e.getMessage(), e);
      }
   }

   @Override
   public String toJsonString() {
      return this.ecKey.toJSONString();
   }

   @Override
   public JsonWebKey.KeyType keyType() {
      return new JsonWebKey.KeyType(this.ecKey.getKeyType().getValue());
   }

   @Override
   public Optional<PublicKey> publicKey() {
      try {
         return Optional.of(this.ecKey.toPublicKey());
      } catch (JOSEException e) {
         LOG.warn(EDefaultLogType.GENERIC_LOG_TYPE, "Could not get public key from Jwk", e);
         return Optional.empty();
      }
   }

   @Override
   public Optional<PrivateKey> privateKey() {
      try {
         return Optional.ofNullable(this.ecKey.toPrivateKey());
      } catch (JOSEException e) {
         LOG.warn(EDefaultLogType.GENERIC_LOG_TYPE, "Could not get private key from Jwk", e);
         return Optional.empty();
      }
   }

   @Override
   public Optional<JwsVerifier> verifier() {
      return this.publicKey().map(key -> JwsVerifierImpl.ecdsaJwsVerifier((ECPublicKey)key));
   }

   @SuppressFBWarnings(justification = "generated code")
   private ECJsonWebKey(ECKey ecKey) {
      this.ecKey = ecKey;
   }
}
