package com.airlock.iam.oauth2.infrastructure.resource.rest;

import com.airlock.iam.common.api.infrastructure.annotation.DisableCsrfFilter;
import com.airlock.iam.common.api.infrastructure.annotation.RequiresWritableSession;
import com.airlock.iam.common.infrastructure.response.CacheControlResponseHeaderService;
import com.airlock.iam.common.infrastructure.service.JWKSetExtractor;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.infrastructure.transfer.documentation.OAuth2JwksResponseData;
import com.airlock.iam.oauth2.infrastructure.validation.AuthorizationServerIdValidator;
import com.nimbusds.jose.jwk.JWKSet;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder;

@Path("/authorization-servers/{authorizationServerId}/jwks")
public class OAuth2JwksResource {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2JwksResource.class);
   private static final String JWKS_EXAMPLE = "{\n\t\"keys\" : [\n\t\t{\n\t\t\t\"kty\" : \"EC\",\n\t\t\t\"use\" : \"sig\",\n\t\t\t\"crv\" : \"P-521\",\n\t\t\t\"kid\" : \"489600a1536e946749ed99e59105018d84713a90\",\n\t\t\t\"x\" : \"AQvgoB_NfBuBOWZE_3ONQUxLRiE02uekRxLgQTfTXZuEvYAOGUWZX2OHAyR-n9jM0gKEyQjArl7OhZEB8aNMmpqo\",\n\t\t\t\"y\" : \"Aftx9XvorV7I7tvfP1doqjzJLnFacSPGYDwNXA1icvGhtObjA1zsUHOQfzP5zeIzC9hSKgqcpdjmrEWipFiN9JTA\",\n\t\t\t\"alg\":\"ES512\"\n\t\t},\n\t\t{\n\t\t\t\"kty\" : \"RSA\",\n\t\t\t\"e\" : \"AQAB\",\n\t\t\t\"use\" : \"sig\",\n\t\t\t\"kid\" : \"729649aacb86a3af49f7bcfeeb2b018d83c410e4\",\n\t\t\t\"alg\" : \"RS256\",\n\t\t\t\"n\" : \"6NWRqi5AuwbjN8-PdkXHmH9Irtr8xlYZmAQtvXVPOX4Q6I_wQA05v2Zwpu8glm_cqHMRPUDywjDMFxCszoeFf9kViQ-bXt-xi-pZyfAo5Nu0KPtu1zaz1vF4aSqXybUV-6xq2L5P2uL_RSScUaJ-cqj09wHya6SXHEtl9WUqqYqcoP2as351PeguktunXxmqjVxfYuXzk6w5KuKunNfT1pgk9Z8hCgmaArHVc9Ane6xlMr4Nkz7hrJoBaoxi7MXBdGitpeBzNQhx5JR1ISN9CIbcsNaY5mJS4j7T6QbUXkNDKQz7cfKQQ5JHYl0gHTxDNvBVXNAXokCZlz_wutHdXw\"\n\t\t}\n\t]\n}\n";
   private final Provider<JWKSetExtractor> jwksSetExtractor;
   private final Provider<CacheControlResponseHeaderService> cacheControlResponseHeaderService;
   private final Provider<AuthorizationServerIdValidator> authorizationServerIdValidator;

   @Inject
   public OAuth2JwksResource(
      Provider<JWKSetExtractor> jwksSetExtractor,
      Provider<CacheControlResponseHeaderService> cacheControlResponseHeaderService,
      Provider<AuthorizationServerIdValidator> authorizationServerIdValidator
   ) {
      this.jwksSetExtractor = jwksSetExtractor;
      this.cacheControlResponseHeaderService = cacheControlResponseHeaderService;
      this.authorizationServerIdValidator = authorizationServerIdValidator;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Retrieved the JWK Set document.",
               content = @Content(
                  mediaType = "application/json",
                  schema = @Schema(implementation = OAuth2JwksResponseData.class),
                  examples = @ExampleObject(
                     name = "A JWK Set",
                     value = "{\n\t\"keys\" : [\n\t\t{\n\t\t\t\"kty\" : \"EC\",\n\t\t\t\"use\" : \"sig\",\n\t\t\t\"crv\" : \"P-521\",\n\t\t\t\"kid\" : \"489600a1536e946749ed99e59105018d84713a90\",\n\t\t\t\"x\" : \"AQvgoB_NfBuBOWZE_3ONQUxLRiE02uekRxLgQTfTXZuEvYAOGUWZX2OHAyR-n9jM0gKEyQjArl7OhZEB8aNMmpqo\",\n\t\t\t\"y\" : \"Aftx9XvorV7I7tvfP1doqjzJLnFacSPGYDwNXA1icvGhtObjA1zsUHOQfzP5zeIzC9hSKgqcpdjmrEWipFiN9JTA\",\n\t\t\t\"alg\":\"ES512\"\n\t\t},\n\t\t{\n\t\t\t\"kty\" : \"RSA\",\n\t\t\t\"e\" : \"AQAB\",\n\t\t\t\"use\" : \"sig\",\n\t\t\t\"kid\" : \"729649aacb86a3af49f7bcfeeb2b018d83c410e4\",\n\t\t\t\"alg\" : \"RS256\",\n\t\t\t\"n\" : \"6NWRqi5AuwbjN8-PdkXHmH9Irtr8xlYZmAQtvXVPOX4Q6I_wQA05v2Zwpu8glm_cqHMRPUDywjDMFxCszoeFf9kViQ-bXt-xi-pZyfAo5Nu0KPtu1zaz1vF4aSqXybUV-6xq2L5P2uL_RSScUaJ-cqj09wHya6SXHEtl9WUqqYqcoP2as351PeguktunXxmqjVxfYuXzk6w5KuKunNfT1pgk9Z8hCgmaArHVc9Ane6xlMr4Nkz7hrJoBaoxi7MXBdGitpeBzNQhx5JR1ISN9CIbcsNaY5mJS4j7T6QbUXkNDKQz7cfKQQ5JHYl0gHTxDNvBVXNAXokCZlz_wutHdXw\"\n\t\t}\n\t]\n}\n"
                  )
               )
            ),
            @ApiResponse(
               responseCode = "404",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>AUTHORIZATION_SERVER_NOT_FOUND</td>\n<td>The authorization server ID does not exist.</td>\n</tr>\n</table>\n</div>\n",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(ref = "json-api.yaml#/components/schemas/ErrorDocumentResponse")),
                     @Content(mediaType = "application/json", schema = @Schema(ref = "#/components/schemas/OAuth2ErrorResponse"))
               }
            )
      }
   )
   @GET
   @Operation(
      description = "Returns the JSON Web Key (JWK) Set document of the authorization server.\n<div class=\"iam-resource-return\">A JWK Set document containing the signing key(s) the RP can use to validate signatures (e.g. ID Token or\n JWT Access Token signatures) from the OP.</div>",
      tags = "Signing Keys",
      summary = "Get JWK Set"
   )
   @Produces("application/json")
   @DisableCsrfFilter
   @RequiresWritableSession
   public Response jwks(
      @Context ContainerRequestContext requestContext,
      @Parameter(description = "The authorization server ID.") @PathParam("authorizationServerId") String authorizationServerId
   ) {
      ((AuthorizationServerIdValidator)this.authorizationServerIdValidator.get()).validateAuthorizationServer(authorizationServerId);
      JWKSet jwkSet = ((JWKSetExtractor)this.jwksSetExtractor.get()).getJwkSet();
      if (jwkSet.getKeys().isEmpty()) {
         LOG.info(
            "Returning empty JWKS response, because 'OpenID Connect ID Token Private Key Signature' is not configured or 'JWT Access Token Format' is not configured or does not use a private key signature."
         );
      }

      return ((CacheControlResponseHeaderService)this.cacheControlResponseHeaderService.get())
         .setCacheControlHeader(defaultResponse(jwkSet), requestContext)
         .build();
   }

   private static ResponseBuilder defaultResponse(JWKSet jwkSet) {
      return Response.ok(jwkSet.toString());
   }
}
