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

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.api.infrastructure.annotation.DisableResponseDelay;
import com.airlock.iam.common.api.infrastructure.annotation.DisableSystemAvailabilityCheck;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.flow.api.domain.model.flow.FlowId;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.infrastructure.mapper.FlowResultMapper;
import com.airlock.iam.flow.application.service.GenericFlowSelectionService;
import com.airlock.iam.flow.application.service.flow.FlowTerminationService;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.publicselfservice.api.infrastructure.generated.transfer.response.PublicSelfServiceFlowResultDataResourceDocument;
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.constraints.Size;
import jakarta.ws.rs.DELETE;
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;
import java.util.Optional;

@Path("/")
public final class PublicSelfServiceFlowResource {
   private final HelpdeskLogger helpdeskLogger;
   private final Provider<PrincipalInfo> principalInfo;
   private final Provider<FlowTerminationService> flowTerminationService;
   private final Provider<GenericFlowSelectionService> flowSelectionService;
   private final FlowResultMapper mapper;

   @Inject
   public PublicSelfServiceFlowResource(
      HelpdeskLogger helpdeskLogger,
      Provider<PrincipalInfo> principalInfo,
      Provider<FlowTerminationService> flowTerminationService,
      Provider<GenericFlowSelectionService> flowSelectionService,
      FlowResultMapper mapper
   ) {
      this.helpdeskLogger = helpdeskLogger;
      this.principalInfo = principalInfo;
      this.flowTerminationService = flowTerminationService;
      this.flowSelectionService = flowSelectionService;
      this.mapper = mapper;
   }

   @ApiResponse(responseCode = "204", description = "Public self-service flow successfully terminated.")
   @Operation(
      description = "Aborts the current public self-service flow.\n<div class=\"iam-resource-return\">response</div>",
      tags = "Flow Control",
      summary = "Abort flow"
   )
   @DELETE
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Path("/flow")
   @DisableResponseDelay
   @DisableSystemAvailabilityCheck
   public Response abort() {
      Optional<UserId> userId = ((PrincipalInfo)this.principalInfo.get()).getKnownIdentity().map(UserIdentity::id);
      if (((FlowTerminationService)this.flowTerminationService.get()).abortFlow()) {
         this.helpdeskLogger.log(userId.orElse(null), "Public self-service flow aborted");
      }

      return Response.noContent().build();
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Public self-service flow selection successful.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = PublicSelfServiceFlowResultDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = PublicSelfServiceFlowResultDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "400",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>UNEXPECTED_CALL</td>\n<td>A flow for another public self-service is already in progress.</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 = "json-api.yaml#/components/schemas/ErrorDocumentResponse"))
               }
            ),
            @ApiResponse(
               responseCode = "403",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>FLOW_CONTINUATION_NOT_ALLOWED</td>\n<td>Not allowed to continue the flow with the provided token.</td>\n</tr>\n<tr>\n<td>FLOW_CONTINUATION_TOKEN_EXPIRED</td>\n<td>The token has expired and the flow cannot be continued.</td>\n</tr>\n<tr>\n<td>USER_NOT_FOUND</td>\n<td>The user associated with the flow was not found.</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 = "json-api.yaml#/components/schemas/ErrorDocumentResponse"))
               }
            ),
            @ApiResponse(
               responseCode = "404",
               description = "No flow with the requested ID exists.",
               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 = "Selects a public self-service flow.\n<p>This endpoint can additionally be called with a flow continuation token attached as <tt>X-Flow-Continuation-Token</tt>\nheader to the request. This token is interpreted by a \"Flow Continuation Step\" that is typically the first step of a public\nself-service flow. Such flows are initialized with the same data, e.g. user identity, as the flow that issued\nthe continuation token.</p>\n<div class=\"iam-resource-return\">A success response with a next step indicating the required next action.</div>",
      tags = "Flow Control",
      summary = "Select flow"
   )
   @POST
   @Path("/flows/{flowId}/select")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @DisableResponseDelay
   public Response selectFlow(
      @Parameter(description = "the ID of the selected flow as configured.") @PathParam("flowId") @Size(min = 1, max = 30) String flowId
   ) {
      GenericFlowSelectionService service = (GenericFlowSelectionService)this.flowSelectionService.get();
      FlowResult result = service.accessFlow(new FlowId(flowId));
      return this.mapper.toResponse(result);
   }
}
