package com.airlock.iam.oauth2.domain.model.token;

import com.nimbusds.jose.JOSEObject;
import com.nimbusds.jose.Payload;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.text.ParseException;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;

public final class RawOAuth2JwtAccessToken implements RawOAuth2Token {
   private static final String JTI_CLAIM = "jti";
   private final JOSEObject joseObject;

   public static RawOAuth2JwtAccessToken parseToken(String tokenString) {
      Optional<JOSEObject> joseObject = toJOSEObject(tokenString);
      if (joseObject.map(RawOAuth2JwtAccessToken::isValid).orElse(false)) {
         return new RawOAuth2JwtAccessToken(joseObject.get());
      } else {
         throw new IllegalArgumentException("Not a JWT Token.");
      }
   }

   public static boolean isValidToken(String tokenString) {
      return toJOSEObject(tokenString).map(RawOAuth2JwtAccessToken::isValid).orElse(false);
   }

   private static Optional<JOSEObject> toJOSEObject(String tokenString) {
      if (tokenString == null) {
         return Optional.empty();
      }

      try {
         return Optional.of(JOSEObject.parse(tokenString));
      } catch (ParseException e) {
         return Optional.empty();
      }
   }

   private static boolean isValid(JOSEObject joseObject) {
      return jtiClaim(joseObject).filter(StringUtils::isNotBlank).isPresent();
   }

   private static Optional<String> jtiClaim(JOSEObject joseObject) {
      return Optional.ofNullable(joseObject)
         .map(JOSEObject::getPayload)
         .<Map>map(Payload::toJSONObject)
         .flatMap(jsonObject -> Optional.ofNullable(jsonObject.get("jti")))
         .filter(String.class::isInstance)
         .map(String.class::cast);
   }

   @Override
   public OAuth2TokenKey getTokenKey() {
      return OAuth2TokenKey.tokenKey(jtiClaim(this.joseObject).orElseThrow());
   }

   @Override
   public String getSecretTokenString() {
      return this.joseObject.getParsedString();
   }

   @Override
   public String toTokenString() {
      return this.joseObject.getParsedString();
   }

   @Override
   public String toString() {
      return this.toTokenString();
   }

   @SuppressFBWarnings(justification = "generated code")
   private RawOAuth2JwtAccessToken(JOSEObject joseObject) {
      this.joseObject = joseObject;
   }
}
