package com.airlock.iam.oauth2.infrastructure.discovery;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
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.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.domain.model.logging.OAuth2LogTypes;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URI;

public final class OpenIDConnectProviderMetadataRequestClient {
   private static final ELogger LOG = ELoggerFactory.getLogger(OpenIDConnectProviderMetadataRequestClient.class);
   private final HttpClient httpClient;
   private final URI discoveryURL;

   public OIDCProviderMetadata executeRequest() throws OpenIDConnectProviderMetadataRequestException {
      try {
         String discoveryContent = this.executeRequestAndGetResponse().body();
         OIDCProviderMetadata metadata = OIDCProviderMetadata.parse(discoveryContent);
         metadata.applyDefaults();
         return metadata;
      } catch (IOException | ParseException e) {
         throw new OpenIDConnectProviderMetadataRequestException(e);
      }
   }

   private HttpResponse executeRequestAndGetResponse() throws IOException {
      HttpRequest discoveryRequest = HttpRequest.builder().uri(HttpRequestUri.httpRequestUri(this.discoveryURL)).method(HttpRequestMethod.GET).build();

      HttpResponse discoveryResponse;
      try {
         discoveryResponse = this.httpClient.execute(discoveryRequest);
      } catch (FailureException e) {
         LOG.warn(OAuth2LogTypes.OAUTH2_MESSAGE, String.format("Cannot connect to %s: %s", this.discoveryURL, e.getMessage()));
         throw e;
      }

      int statusCode = discoveryResponse.status();
      if (200 != statusCode) {
         LOG.warn(OAuth2LogTypes.OAUTH2_MESSAGE, String.format("Unexpected status code: %s", statusCode));
         throw new IOException("Unexpected status code: " + statusCode);
      } else {
         return discoveryResponse;
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public OpenIDConnectProviderMetadataRequestClient(HttpClient httpClient, URI discoveryURL) {
      this.httpClient = httpClient;
      this.discoveryURL = discoveryURL;
   }
}
