package com.airlock.iam.publicselfservice.infrastructure.resource;

import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.infrastructure.mapper.FlowResultMapper;
import com.airlock.iam.publicselfservice.api.infrastructure.generated.transfer.response.PublicSelfServiceFlowResultDataResourceDocument;
import com.airlock.iam.publicselfservice.application.service.DeviceTokenVerification;
import com.airlock.iam.publicselfservice.infrastructure.generated.transfer.PublicSelfServiceDeviceTokenChallengeDataResourceDocument;
import com.airlock.iam.publicselfservice.infrastructure.transfer.PublicSelfServiceDeviceTokenChallengeData;
import com.airlock.iam.publicselfservice.infrastructure.transfer.request.DeviceTokenResponseCheckRequest;
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.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
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("/device-token")
public class PublicSelfServiceDeviceTokenFlowResource {
   private final FlowResultMapper mapper;
   private final Provider<DeviceTokenVerification> deviceTokenVerification;

   @Inject
   public PublicSelfServiceDeviceTokenFlowResource(FlowResultMapper mapper, Provider<DeviceTokenVerification> deviceTokenVerification) {
      this.mapper = mapper;
      this.deviceTokenVerification = deviceTokenVerification;
   }

   @ApiResponse(
      responseCode = "200",
      description = "A challenge has been returned.",
      content = {
            @Content(
               mediaType = "application/vnd.api+json",
               schema = @Schema(implementation = PublicSelfServiceDeviceTokenChallengeDataResourceDocument.class)
            ),
            @Content(mediaType = "application/json", schema = @Schema(implementation = PublicSelfServiceDeviceTokenChallengeDataResourceDocument.class))
      }
   )
   @Operation(
      description = "Get device token challenge.\n<p>\nReturns a challenge for the device token verification step. The challenge is always returned, even if there is\nno device token for the provided ID, in order to prevent user enumeration.\n</p>\n<p>\nThe challenge is a random alphanumeric string. It has to be included in a JWT and signed by the device token key.\n</p>\n<div class=\"iam-resource-return\">A challenge string.</div>",
      tags = "Device Tokens",
      summary = "Get challenge"
   )
   @POST
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Path("/{deviceTokenId}/challenge/retrieve")
   public Response retrieveChallenge(
      @Parameter(description = "The ID of the client device token.") @PathParam("deviceTokenId") @Size(min = 1, max = 50) String deviceTokenId
   ) {
      FlowResult result = ((DeviceTokenVerification)this.deviceTokenVerification.get()).getChallenge(deviceTokenId);
      return this.mapper.toResponseWithData(result, PublicSelfServiceDeviceTokenChallengeData.class);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Device Token successfully verified.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = PublicSelfServiceFlowResultDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = PublicSelfServiceFlowResultDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "403",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>DEVICE_TOKEN_VERIFICATION_FAILED</td>\n<td>\nDevice token verification failed for one of the following reasons:\n\t\t\t<ul>\n \t\t\t\t<li>Signature on JWT is wrong</li>\n \t\t\t\t<li>Signed wrong challenge</li>\n \t\t\t\t<li>Challenge expired</li>\n \t\t\t\t<li>Device token expired</li>\n \t\t\t\t<li>Device token disabled</li>\n\t\t\t</ul>\n\t\t</td>\n\t</tr>\n</table>\n</div>\n<br>Possible next step: none.\n",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(ref = "json-api.yaml#/components/schemas/ErrorDocumentResponse")),
                     @Content(mediaType = "application/json", schema = @Schema(ref = "json-api.yaml#/components/schemas/ErrorDocumentResponse"))
               }
            )
      }
   )
   @Operation(
      description = "Verify device token JWT.\n<p>\nVerifies the submitted JWT. The JWT must contain at least the following claims:\n<ul>\n<li>\"challenge\": The challenge.</li>\n</ul>\nIt must be signed by the device token key using the same algorithm as in the device token creation process.\n</p>\n<div class=\"iam-resource-return\">Whether the check was successful.</div>",
      tags = "Device Tokens",
      summary = "Verify JWT"
   )
   @POST
   @Path("/check")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response checkJwt(@Parameter(description = "The JWT to be verified.") @NotNull @Valid DeviceTokenResponseCheckRequest request) {
      FlowResult result = ((DeviceTokenVerification)this.deviceTokenVerification.get()).checkJwt(request.getJwt());
      return this.mapper.toResponse(result);
   }
}
