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.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.application.service.metadata.OAuth2MetadataService;
import com.airlock.iam.oauth2.domain.model.metadata.OAuth2MetadataIssuerValidationException;
import com.airlock.iam.oauth2.infrastructure.transfer.documentation.OAuth2DiscoveryAndMetadataEndpointResponseData;
import com.airlock.iam.oauth2.infrastructure.validation.AuthorizationServerIdValidator;
import com.airlock.iam.oauth2.infrastructure.validation.availability.OAuth2MetadataEndpointAvailability;
import com.nimbusds.oauth2.sdk.as.AuthorizationServerMetadata;
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.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;
import jakarta.ws.rs.core.Response.Status;

@Path("/authorization-servers/{authorizationServerId}/.well-known/oauth-authorization-server")
public class OAuth2MetadataEndpointResource {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2MetadataEndpointResource.class);
   private final Provider<OAuth2MetadataEndpointAvailability> metadataEndpointAvailability;
   private final Provider<OAuth2MetadataService> metadataService;
   private final Provider<CacheControlResponseHeaderService> cacheControlResponseHeaderService;
   private final Provider<AuthorizationServerIdValidator> authorizationServerIdValidator;

   @Inject
   public OAuth2MetadataEndpointResource(
      Provider<OAuth2MetadataEndpointAvailability> metadataEndpointAvailability,
      Provider<OAuth2MetadataService> metadataService,
      Provider<CacheControlResponseHeaderService> cacheControlResponseHeaderService,
      Provider<AuthorizationServerIdValidator> authorizationServerIdValidator
   ) {
      this.metadataEndpointAvailability = metadataEndpointAvailability;
      this.metadataService = metadataService;
      this.cacheControlResponseHeaderService = cacheControlResponseHeaderService;
      this.authorizationServerIdValidator = authorizationServerIdValidator;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "The metadata information have been returned.",
               content = @Content(mediaType = "application/json", schema = @Schema(implementation = OAuth2DiscoveryAndMetadataEndpointResponseData.class))
            ),
            @ApiResponse(
               responseCode = "400",
               description = "The request was invalid.",
               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"))
               }
            ),
            @ApiResponse(
               responseCode = "404",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>-</td>\n<td>No metadata endpoint available.</td>\n</tr>\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"))
               }
            ),
            @ApiResponse(responseCode = "500", description = "The metadata endpoint is not configured correctly.")
      }
   )
   @GET
   @Operation(
      description = "Returns the metadata that an OAuth 2.0 or OpenID Connect client can use to obtain the information needed\nto interact with an authorization server, including its endpoint locations and authorization server capabilities.\nRefer to the <a href=\"https://tools.ietf.org/html/rfc8414\" target=\"_blank\">RFC 8414</a> specification\nfor a more detailed description of the endpoint.\n<div class=\"iam-resource-return\">The set of claims about the OAuth 2.0 or OpenID Provider's configuration.\n Depending on the supported specifications, other claims may also be returned.</div>",
      tags = "OAuth 2.0/OIDC",
      summary = "Get metadata"
   )
   @Produces("application/json")
   @DisableCsrfFilter
   @RequiresWritableSession
   public Response metadataEndpoint(
      @Context ContainerRequestContext requestContext,
      @Parameter(description = "The authorization server ID.") @PathParam("authorizationServerId") String authorizationServerId
   ) {
      ((AuthorizationServerIdValidator)this.authorizationServerIdValidator.get()).validateAuthorizationServer(authorizationServerId);
      if (!((OAuth2MetadataEndpointAvailability)this.metadataEndpointAvailability.get()).isAvailable()) {
         LOG.info("No metadata resource available.");
         return Response.status(Status.NOT_FOUND).build();
      }

      try {
         AuthorizationServerMetadata metadata = ((OAuth2MetadataService)this.metadataService.get()).provideMetadata();
         return ((CacheControlResponseHeaderService)this.cacheControlResponseHeaderService.get())
            .setCacheControlHeader(defaultResponse(metadata), requestContext)
            .build();
      } catch (OAuth2MetadataIssuerValidationException e) {
         return Response.serverError().build();
      }
   }

   private static ResponseBuilder defaultResponse(AuthorizationServerMetadata metadata) {
      return Response.ok(metadata.toJSONObject().toJSONString());
   }
}
