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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.domain.model.jwk.JsonWebKeySet;
import com.airlock.iam.common.domain.service.cache.FailureTolerantExpiringCache;
import com.airlock.iam.core.domain.model.http.HttpRequest;
import com.airlock.iam.core.domain.model.http.HttpRequestMethod;
import com.airlock.iam.core.domain.model.http.HttpRequestUri;
import com.airlock.iam.core.domain.model.http.HttpResponse;
import com.airlock.iam.core.domain.model.http.client.HttpClient;
import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import java.net.URI;
import java.time.Duration;
import org.apache.commons.lang3.StringUtils;

public final class HttpClientRemoteJWKSetLoader implements RemoteJWKSetLoader {
   private static final ELogger LOG = ELoggerFactory.getLogger(HttpClientRemoteJWKSetLoader.class);
   private final FailureTolerantExpiringCache<URI, JsonWebKeySet> jwkSetCache;

   public HttpClientRemoteJWKSetLoader(HttpClient httpClient, Duration cacheRefreshInterval) {
      this.jwkSetCache = new FailureTolerantExpiringCache<>(
         cacheRefreshInterval,
         jwksUri -> loadJwkSet(httpClient, jwksUri),
         (var0, failure) -> LOG.warn(
            EDefaultLogType.GENERIC_LOG_TYPE, "Could not refresh JWK set; keeping the previous data in the cache: " + failure.getMessage()
         )
      );
   }

   @Override
   public RemoteJWKSetLoader.RemoteJsonWebKeySet getJsonWebKeySet(URI jwksUri) {
      FailureTolerantExpiringCache.ValueWithRefreshInfo<JsonWebKeySet> access = this.jwkSetCache.getWithRefreshInfo(jwksUri);
      return new RemoteJWKSetLoader.RemoteJsonWebKeySet(access.value(), access.hasBeenRefreshed());
   }

   @Override
   public JsonWebKeySet reloadJsonWebKeySet(URI jwksUri) {
      LOG.info("Reloading JWKS from " + LogUtil.logSafeQuote(jwksUri.toASCIIString()));
      return this.jwkSetCache.refresh(jwksUri);
   }

   private static JsonWebKeySet loadJwkSet(HttpClient httpClient, URI location) {
      LOG.debug("Loading JWKS from URI " + LogUtil.logSafeQuote(location.toASCIIString()));
      HttpResponse response = httpClient.execute(HttpRequest.builder().uri(HttpRequestUri.httpRequestUri(location)).method(HttpRequestMethod.GET).build());
      return JsonWebKeySet.fromJson(response.body())
         .orElseThrow(
            () -> new FailureException("Failed to parse JWKS from response body: " + LogUtil.logSafeQuote(StringUtils.abbreviate(response.body(), 30)))
         );
   }
}
