package com.airlock.iam.ssi.infrastructure.rest;

import com.airlock.iam.base.domain.service.uri.IamUriBuilder;
import com.airlock.iam.base.misc.json.JsonCoding;
import com.airlock.iam.core.domain.model.http.HttpRequest;
import com.airlock.iam.core.domain.model.http.HttpRequestBody;
import com.airlock.iam.core.domain.model.http.HttpRequestHeader;
import com.airlock.iam.core.domain.model.http.HttpRequestHeaders;
import com.airlock.iam.core.domain.model.http.HttpRequestMethod;
import com.airlock.iam.core.domain.model.http.HttpRequestParameter;
import com.airlock.iam.core.domain.model.http.HttpRequestUri;
import com.airlock.iam.ssi.infrastructure.service.procivis.transfer.request.CreateProofRequest;
import com.airlock.iam.ssi.infrastructure.service.procivis.transfer.request.CredentialClaim;
import com.airlock.iam.ssi.infrastructure.service.procivis.transfer.request.IssueCredentialRequest;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.stream.Stream;

public abstract class AbstractProcivisRequestFactory implements ProcivisRequestFactory {
   protected abstract URI procivisUrl();

   protected abstract Optional<String> accessToken(boolean var1);

   @Override
   public HttpRequest listProofSchemasRequestFor(String proofSchemaName, boolean forceAuthTokenRefresh) {
      return this.listProofSchemasRequestFor(forceAuthTokenRefresh, HttpRequestParameter.httpRequestParameter("name", proofSchemaName));
   }

   private HttpRequest listProofSchemasRequestFor(boolean forceAuthTokenRefresh, HttpRequestParameter... params) {
      return this.getRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.PROOF_SCHEMA.path()).build().toString(), this.withListEndpointParams(params)))
         .build();
   }

   @Override
   public HttpRequest listDidsRequestFor(String did, boolean forceAuthTokenRefresh) {
      return this.listDidsRequestFor(forceAuthTokenRefresh, HttpRequestParameter.httpRequestParameter("did", did));
   }

   private HttpRequest listDidsRequestFor(boolean forceAuthTokenRefresh, HttpRequestParameter... params) {
      return this.getRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.DID.path()).build().toString(), this.withListEndpointParams(params)))
         .build();
   }

   @Override
   public HttpRequest createProofRequestFor(String proofSchemaId, String didId, String exchangeProtocol, boolean forceAuthTokenRefresh) {
      return this.postRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.PROOF_REQUEST.path()).build()))
         .body(this.toBody(new CreateProofRequest(proofSchemaId, didId, exchangeProtocol)))
         .build();
   }

   @Override
   public HttpRequest shareProofRequestFor(String proofRequestId, boolean forceAuthTokenRefresh) {
      return this.postRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.PROOF_REQUEST.path()).path(proofRequestId).path("share").build()))
         .build();
   }

   @Override
   public HttpRequest retrieveProofRequestFor(String proofRequestId, boolean forceAuthTokenRefresh) {
      return this.getRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.PROOF_REQUEST.path()).path(proofRequestId).build()))
         .build();
   }

   @Override
   public HttpRequest listCredentialSchemasRequestFor(String credentialSchemaName, boolean forceAuthTokenRefresh) {
      return this.listCredentialSchemasRequestFor(forceAuthTokenRefresh, HttpRequestParameter.httpRequestParameter("name", credentialSchemaName));
   }

   private HttpRequest listCredentialSchemasRequestFor(boolean forceAuthTokenRefresh, HttpRequestParameter... params) {
      return this.getRequestBuilder(forceAuthTokenRefresh)
         .uri(
            HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.CREDENTIAL_SCHEMA.path()).build().toString(), this.withListEndpointParams(params))
         )
         .build();
   }

   protected HttpRequestParameter[] withListEndpointParams(HttpRequestParameter[] params) {
      return params;
   }

   @Override
   public HttpRequest retrieveCredentialSchemaRequestFor(String credentialSchemaId, boolean forceAuthTokenRefresh) {
      return this.getRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.CREDENTIAL_SCHEMA.path()).path(credentialSchemaId).build()))
         .build();
   }

   @Override
   public HttpRequest createCredentialRequestFor(
      String credentialSchemaId, String didId, CredentialClaim[] claims, String exchangeProtocol, boolean forceAuthTokenRefresh
   ) {
      return this.postRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.CREDENTIAL.path()).build()))
         .body(this.toBody(new IssueCredentialRequest(credentialSchemaId, didId, exchangeProtocol, claims)))
         .build();
   }

   @Override
   public HttpRequest issueCredentialRequestFor(String credentialId, boolean forceAuthTokenRefresh) {
      return this.postRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.CREDENTIAL.path()).path(credentialId).path("share").build()))
         .build();
   }

   @Override
   public HttpRequest retrieveCredentialRequestFor(String credentialId, boolean forceAuthTokenRefresh) {
      return this.getRequestBuilder(forceAuthTokenRefresh)
         .uri(HttpRequestUri.httpRequestUri(this.procivisPath(ProcivisApiPath.CREDENTIAL.path()).path(credentialId).build()))
         .build();
   }

   private IamUriBuilder procivisPath(URI pathToAppend) {
      return IamUriBuilder.fromUri(this.procivisUrl()).path(pathToAppend.toString());
   }

   private HttpRequestBody toBody(Object requestBody) {
      return HttpRequestBody.httpRequestBody(JsonCoding.toJson(requestBody).getBytes(StandardCharsets.UTF_8));
   }

   private HttpRequest.HttpRequestBuilder getRequestBuilder(boolean forceTokenRefresh) {
      return HttpRequest.builder()
         .headers(
            HttpRequestHeaders.httpRequestHeaders(
               this.accessToken(forceTokenRefresh).map(token -> HttpRequestHeader.httpRequestHeader("Authorization", bearer(token))).stream().toList()
            )
         )
         .method(HttpRequestMethod.GET);
   }

   private HttpRequest.HttpRequestBuilder postRequestBuilder(boolean forceTokenRefresh) {
      return HttpRequest.builder()
         .headers(
            HttpRequestHeaders.httpRequestHeaders(
               Stream.concat(
                     Stream.of(HttpRequestHeader.httpRequestHeader("Content-Type", "application/json")),
                     this.accessToken(forceTokenRefresh).map(token -> HttpRequestHeader.httpRequestHeader("Authorization", bearer(token))).stream()
                  )
                  .toList()
            )
         )
         .method(HttpRequestMethod.POST);
   }

   private static String bearer(String token) {
      return "Bearer " + token;
   }
}
