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

import com.airlock.iam.authentication.application.service.ApplicationAccessService;
import com.airlock.iam.authentication.infrastructure.service.ForwardLocationValidator;
import com.airlock.iam.authentication.infrastructure.transfer.request.ForwardLocationAccessRequest;
import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.base.infrastructure.error.GenericRestErrorCode;
import com.airlock.iam.base.infrastructure.mapper.ValidationToErrorMapper;
import com.airlock.iam.common.api.infrastructure.annotation.DisableResponseDelay;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.infrastructure.mapper.FlowResultMapper;
import com.airlock.iam.flow.auth.api.infrastructure.generated.transfer.response.AuthenticationFlowResultDataResourceDocument;
import com.airlock.iam.login.api.domain.model.location.ForwardLocation;
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.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import java.util.Optional;

@Path("/location/access")
public class ApplicationAccessByURIFlowResource {
   private final Provider<ApplicationAccessService> service;
   private final Provider<FlowResultMapper> mapper;

   @Inject
   public ApplicationAccessByURIFlowResource(Provider<FlowResultMapper> mapper, Provider<ApplicationAccessService> service) {
      this.mapper = mapper;
      this.service = service;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Authorization is complete, target application can be accessed.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "400",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>UNEXPECTED_CALL</td>\n<td>A flow with either a missing or different location URI is already in progress. To start a new flow, the running flow must first be terminated.</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 = "401",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>NOT_AUTHORIZED</td>\n<td>Access not yet granted, more authentication/authorization steps are required.</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 = "Access to this target application cannot be granted.",
               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 = "Access a target application by its forward location URI. If a flow for the same location URI\nis already in progress it will be resumed. If a flow for either an unspecified or different\nlocation URI is already in progress, an error will be returned.\n<p>\nRetrieving information about ongoing flows can be done by calling\n<tt><span class=\"httpMethod small GET\">GET</span>/public/flow/</tt>.\n</p>\n<div class=\"iam-resource-return\">A success response without next step if the client is authorized to access the\n target application or an error response if further steps are required.</div>",
      tags = "Location",
      summary = "Access application"
   )
   @POST
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Consumes("application/json")
   @DisableResponseDelay
   public Response access(@Parameter(description = "Contains the forward URI.") @NotNull @Valid ForwardLocationAccessRequest request) {
      Optional<ForwardLocation> forwardLocation = ForwardLocationValidator.validateForwardLocationURI(request.getLocation());
      if (forwardLocation.isPresent()) {
         FlowResult result = ((ApplicationAccessService)this.service.get()).startNewFlowOrReuseIfSameLocationUri(forwardLocation.get());
         return ((FlowResultMapper)this.mapper.get()).toResponse(result);
      } else {
         return ErrorResponse.badRequest()
            .error(ValidationToErrorMapper.createErrorData(GenericRestErrorCode.VALIDATION_FAILED, GenericError.INVALID_VALUE, "/location"))
            .log(String.format("The location '%s' is not a valid URI", request.getLocation()))
            .build()
            .toResponse();
      }
   }
}
