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

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.base.infrastructure.mapper.entity.RestMapper;
import com.airlock.iam.common.application.jsonapi.ResourceIdentifierUtils;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientId;
import com.airlock.iam.oauth2.application.service.OpenIdConnectSessionManagementService;
import com.airlock.iam.oauth2.domain.model.validation.CheckOriginResult;
import com.airlock.iam.oauth2.infrastructure.generated.transfer.response.OpenIdConnectOriginCheckDataResourceDocument;
import com.airlock.iam.oauth2.infrastructure.transfer.request.OpenIdConnectCheckOriginRequest;
import com.airlock.iam.oauth2.infrastructure.transfer.response.OpenIdConnectOriginCheckData;
import com.airlock.iam.oauth2.infrastructure.validation.AuthorizationServerIdValidator;
import com.airlock.iam.oauth2.infrastructure.validation.availability.OpenIdConnectSessionManagementAvailability;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
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.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;

@Path("/authorization-servers/{authorizationServerId}/check-origin")
public final class OpenIdConnectSessionManagementResource {
   private final RestMapper restMapper;
   private final Provider<OpenIdConnectSessionManagementAvailability> sessionManagementAvailability;
   private final Provider<OpenIdConnectSessionManagementService> sessionManagementService;
   private final Provider<AuthorizationServerIdValidator> authorizationServerIdValidator;

   @Inject
   public OpenIdConnectSessionManagementResource(
      RestMapper restMapper,
      Provider<OpenIdConnectSessionManagementAvailability> sessionManagementAvailability,
      Provider<OpenIdConnectSessionManagementService> sessionManagementService,
      Provider<AuthorizationServerIdValidator> authorizationServerIdValidator
   ) {
      this.restMapper = restMapper;
      this.sessionManagementAvailability = sessionManagementAvailability;
      this.sessionManagementService = sessionManagementService;
      this.authorizationServerIdValidator = authorizationServerIdValidator;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Result indicating if the provided origin is valid.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = OpenIdConnectOriginCheckDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = OpenIdConnectOriginCheckDataResourceDocument.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>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 = "501", description = "The OIDC session management is not configured.")
      }
   )
   @Operation(
      description = "Checks if the provided RP origin is allowed for this authorization server.\n<div class=\"iam-resource-return\">response indicating the success.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "OAuth 2.0/OIDC",
      summary = "Check RP origin"
   )
   @POST
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response checkOrigin(
      @Parameter(description = "The authorization server ID.") @PathParam("authorizationServerId") String authorizationServerId,
      @Parameter(description = "Contains the origin to be verified.") @NotNull @Valid OpenIdConnectCheckOriginRequest request
   ) {
      ((AuthorizationServerIdValidator)this.authorizationServerIdValidator.get()).validateAuthorizationServer(authorizationServerId);
      if (!((OpenIdConnectSessionManagementAvailability)this.sessionManagementAvailability.get()).isAvailable()) {
         return ErrorResponse.notImplemented().build().toResponse();
      }

      CheckOriginResult result = ((OpenIdConnectSessionManagementService)this.sessionManagementService.get())
         .isOriginAllowed(OAuth2ClientId.oAuth2ClientId(request.getClientId()), request.getOrigin());
      ResourceDocument<OpenIdConnectOriginCheckData> document = this.restMapper
         .forElement(result, OpenIdConnectOriginCheckData.class)
         .withIdSupplier(s -> ResourceIdentifierUtils.getIdFor(request))
         .toResourceDocument();
      return Response.ok(document).build();
   }
}
