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

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.nimbusds.jose.util.JSONObjectUtils;
import com.nimbusds.jwt.JWTClaimsSet;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.text.ParseException;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class JwtClaimsSet {
   private final JWTClaimsSet claimSet;

   public static JwtClaimsSet of(String claimSet) {
      try {
         return new JwtClaimsSet(JWTClaimsSet.parse(claimSet));
      } catch (ParseException e) {
         throw new ViolationException("Failed to create claim set", e);
      }
   }

   public static JwtClaimsSet fromMap(Map<String, Object> claims) {
      try {
         return new JwtClaimsSet(JWTClaimsSet.parse(claims));
      } catch (ParseException e) {
         throw new ViolationException("Failed to create claim set", e);
      }
   }

   public String toJsonString() {
      return JSONObjectUtils.toJSONString(this.claimSet.toJSONObject());
   }

   public Optional<Object> claim(String name) {
      return Optional.ofNullable(this.claimSet.getClaim(name));
   }

   public Optional<String> issuer() {
      return Optional.ofNullable(this.claimSet.getIssuer());
   }

   public Optional<String> subject() {
      return Optional.ofNullable(this.claimSet.getSubject());
   }

   public Optional<List<String>> audience() {
      return Optional.ofNullable(this.claimSet.getAudience());
   }

   public Optional<String> jwtId() {
      return Optional.ofNullable(this.claimSet.getJWTID());
   }

   public Map<String, Object> claims() {
      return this.claimSet.getClaims();
   }

   public Optional<Instant> notBeforeTime() {
      return Optional.ofNullable(this.claimSet.getNotBeforeTime()).map(Date::toInstant);
   }

   public Optional<Instant> expirationTime() {
      return Optional.ofNullable(this.claimSet.getExpirationTime()).map(Date::toInstant);
   }

   public Optional<Instant> claimAsInstant(String name) {
      try {
         return Optional.ofNullable(this.claimSet.getDateClaim(name)).map(Date::toInstant);
      } catch (ParseException e) {
         return Optional.empty();
      }
   }

   public Optional<Instant> issueTime() {
      return Optional.ofNullable(this.claimSet.getIssueTime()).map(Date::toInstant);
   }

   @SuppressFBWarnings(justification = "generated code")
   private JwtClaimsSet(JWTClaimsSet claimSet) {
      this.claimSet = claimSet;
   }
}
