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

import com.airlock.iam.authentication.infrastructure.generated.transfer.response.Airlock2FADeviceEditAuthResponseAttributesDataResourceDocument;
import com.airlock.iam.authentication.infrastructure.generated.transfer.response.AuthenticationAirlock2FAActivationChallengeDataResourceDocument;
import com.airlock.iam.authentication.infrastructure.transfer.request.Airlock2FAFlowBindingTokenRequest;
import com.airlock.iam.authentication.infrastructure.transfer.request.AuthenticationAirlock2FADeviceEditRequest;
import com.airlock.iam.authentication.infrastructure.transfer.response.Airlock2FADeviceEditAuthResponseAttributesData;
import com.airlock.iam.authentication.infrastructure.transfer.response.AuthenticationAirlock2FAActivationChallengeData;
import com.airlock.iam.factor.domain.model.airlock2fa.activation.Airlock2FAActivationCode;
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.flow.infrastructure.mapper.ValidationResultMapper;
import com.airlock.iam.flow.shared.application.service.airlock2fa.Airlock2FAActivation;
import com.airlock.iam.flow.shared.application.service.airlock2fa.Airlock2FADeviceEdit;
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.QueryParam;
import jakarta.ws.rs.core.Response;

@Path("/airlock-2fa/activation")
public class Airlock2FAActivationAuthenticationFlowResource {
   private final Provider<Airlock2FAActivation> airlock2faActivation;
   private final Provider<Airlock2FADeviceEdit> deviceEdit;
   private final FlowResultMapper mapper;
   private final Provider<ValidationResultMapper> stepErrorResultMapper;

   @Inject
   public Airlock2FAActivationAuthenticationFlowResource(
      Provider<Airlock2FAActivation> airlock2faActivation,
      Provider<Airlock2FADeviceEdit> deviceEdit,
      FlowResultMapper mapper,
      Provider<ValidationResultMapper> stepErrorResultMapper
   ) {
      this.airlock2faActivation = airlock2faActivation;
      this.deviceEdit = deviceEdit;
      this.mapper = mapper;
      this.stepErrorResultMapper = stepErrorResultMapper;
   }

   @ApiResponse(
      responseCode = "200",
      description = "Further steps required.\n<br>Next step: <tt>AIRLOCK_2FA_DEVICE_ACTIVATION_POLLING_REQUIRED</tt> (as documented <a href=\"#nextAuthStepCodes\">here</a>).\n",
      content = {
            @Content(
               mediaType = "application/vnd.api+json",
               schema = @Schema(implementation = AuthenticationAirlock2FAActivationChallengeDataResourceDocument.class)
            ),
            @Content(mediaType = "application/json", schema = @Schema(implementation = AuthenticationAirlock2FAActivationChallengeDataResourceDocument.class))
      }
   )
   @Deprecated
   @Operation(
      description = "> [!caution]\n> This endpoint is deprecated. The activation challenge is returned as <a href=\"#additionalAuthAttributes\">additional attribute</a> with the\n<tt>AIRLOCK_2FA_DEVICE_ACTIVATION_REQUIRED</tt> and <tt><s>AIRLOCK_2FA_DEVICE_ACTIVATION_POLLING_REQUIRED</s></tt> next step codes.\n<p>Retrieves the Airlock 2FA activation challenge.</p>\n<div class=\"iam-resource-return\">The activation challenge.</div>",
      tags = "Airlock 2FA",
      summary = "Get activation challenge"
   )
   @POST
   @Path("/challenge/retrieve")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response retrieveActivationChallenge() {
      FlowResult flowResult = ((Airlock2FAActivation)this.airlock2faActivation.get()).retrieveActivationChallenge();
      return this.mapper.toResponseWithData(flowResult, AuthenticationAirlock2FAActivationChallengeData.class);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Step completed or further steps required, such as further polling.\n<br>Possible next step: <tt><s>AIRLOCK_2FA_DEVICE_ACTIVATION_POLLING_REQUIRED</s></tt> (will be removed with 9.0,\nclients should be prepared to expect <tt>AIRLOCK_2FA_DEVICE_ACTIVATION_REQUIRED</tt> instead,\nas documented <a href=\"#nextAuthStepCodes\">here</a>).\n",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "403",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>AIRLOCK_2FA_DEVICE_ACTIVATION_FAILED</td>\n<td>The activation is expired.</td>\n</tr>\n</table>\n</div>\n<br>The current authentication flow has been aborted.\n<br>Possible next step: none.\n<br>Next step: <tt>AIRLOCK_2FA_DEVICE_ACTIVATION_REQUIRED</tt>.\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 = "Polls the Airlock 2FA activation status.\n<div class=\"iam-resource-return\">The status of the Airlock 2FA activation.\n If the attribute <tt>nextAuthStep</tt> is present in the response, further steps are required to successfully authenticate.\n If the attribute is missing, the user is successfully authenticated.\n </div>",
      tags = "Airlock 2FA",
      summary = "Poll activation status"
   )
   @POST
   @Path("/status/poll")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response pollActivationStatus(
      @Parameter(
   description = "Whether the final result should be returned (optional, default <tt>false</tt>). See <a href=\"#pollingEndpoints\">polling endpoints</a> for details."
) @QueryParam("queryOnly") boolean queryOnly
   ) {
      FlowResult flowResult = ((Airlock2FAActivation)this.airlock2faActivation.get()).pollActivationResult(queryOnly);
      return this.mapper.toResponse(flowResult);
   }

   @ApiResponse(
      responseCode = "200",
      description = "The information has successfully been returned.",
      content = {
            @Content(
               mediaType = "application/vnd.api+json",
               schema = @Schema(implementation = Airlock2FADeviceEditAuthResponseAttributesDataResourceDocument.class)
            ),
            @Content(mediaType = "application/json", schema = @Schema(implementation = Airlock2FADeviceEditAuthResponseAttributesDataResourceDocument.class))
      }
   )
   @Deprecated
   @Operation(
      description = "> [!caution]\n> This endpoint is deprecated. The current display name is returned as <a href=\"#additionalAuthAttributes\">additional attribute</a> with the <tt>AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE</tt> next step code.\n<p>Retrieves editable attributes of the activated Airlock 2FA device.</p>\n<div class=\"iam-resource-return\">information about the activated Airlock 2FA device.</div>",
      tags = "Airlock 2FA",
      summary = "Get attributes"
   )
   @POST
   @Path("/device-edit/data/retrieve")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response retrieveCurrentDisplayName() {
      FlowResult result = ((Airlock2FADeviceEdit)this.deviceEdit.get()).retrieveCurrentDisplayName();
      return this.mapper.toResponseWithData(result, Airlock2FADeviceEditAuthResponseAttributesData.class);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Device attributes successfully set. The <tt>nextAuthStep</tt> informs about the step's data validity.",
               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>VALIDATION_FAILED</td>\n<td>The supplied attributes could not be validated successfully. See <a href=\"#validationFailures\">validation failures</a> for detail codes.</td>\n</tr>\n</table>\n</div>\n<br>Possible next step: <tt>AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE</tt> (as documented <a href=\"#nextAuthStepCodes\">here</a>).\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 = "The current flow has been aborted.",
               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 = "Edit attributes of the activated Airlock 2FA device.\n<p>This endpoint updates only attributes sent with the request. Omitted attributes remain unchanged.</p>\n<div class=\"iam-resource-return\">Whether the changed device attributes were accepted. If a request contains both valid and invalid attributes, only the valid attributes will be processed.\n This endpoint returns a <tt>nextAuthStep</tt> with value <tt>AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE</tt>.\n A follow-up call to the <tt>continue</tt> endpoint will advance the flow and apply the valid changes.\n <p>\n On error, this endpoint returns a detailed list of validation failures.\n </p></div>",
      tags = "Airlock 2FA",
      summary = "Edit attributes"
   )
   @POST
   @Path("/device-edit/data")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response editDevice(
      @Parameter(description = "Contains the device attributes to be edited. Currently, only the device display name can be changed.") @NotNull @Valid AuthenticationAirlock2FADeviceEditRequest request
   ) {
      FlowResult result = ((Airlock2FADeviceEdit)this.deviceEdit.get()).changeDisplayName(request.getDisplayName());
      return ((ValidationResultMapper)this.stepErrorResultMapper.get()).toValidationResponse(result);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "All data successfully received and flow completed if no further steps required.",
               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>VALIDATION_FAILED</td>\n<td>The supplied attributes could not be validated successfully. See <a href=\"#validationFailures\">validation failures</a> for detail codes.</td>\n</tr>\n</table>\n</div>\n<br>Possible next step: <tt>AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE</tt> (as documented <a href=\"#nextAuthStepCodes\">here</a>).\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 = "The current flow has been aborted.",
               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 = "Validate all data on the current Airlock 2FA device edit step and continue to the next step if possible.\n<div class=\"iam-resource-return\">If the attribute <tt>nextAuthStep</tt> is present in the response, further steps are required\n to successfully authenticate. If the attribute is missing, the user has successfully completed the flow.\n <p>\n On error, this endpoint returns a detailed list of validation failures.\n </p></div>",
      tags = "Airlock 2FA",
      summary = "Validate and continue"
   )
   @POST
   @Path("/device-edit/continue")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response validateAndContinue() {
      return this.mapper.toResponse(((Airlock2FADeviceEdit)this.deviceEdit.get()).validateAndContinue());
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Further steps required.\n<br>Next step: <tt>AIRLOCK_2FA_DEVICE_ACTIVATION_REQUIRED</tt>.\n",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = AuthenticationFlowResultDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "403",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>AIRLOCK_2FA_DEVICE_ACTIVATION_FAILED</td>\n<td>The activation code corresponds to a valid activation of the user but it is expired or has already been used previously to activate a device.</td>\n</tr>\n<tr>\n<td>AIRLOCK_2FA_DEVICE_ACTIVATION_CODE_INVALID</td>\n<td>The provided activation code does not correspond to a valid activation of the user.</td>\n</tr>\n</table>\n</div>\n<br>The current authentication flow has been aborted.\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 = "Starts a device activation with Trusted Session Binding.\n<div class=\"iam-resource-return\">The Trusted Session Binding token.</div>",
      tags = "Airlock 2FA",
      summary = "Start activation"
   )
   @POST
   @Path("/start")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response getBindingToken(
      @Parameter(description = "Contains the activation code for which the flow binding token is retrieved.") @NotNull @Valid Airlock2FAFlowBindingTokenRequest request
   ) {
      FlowResult flowResult = ((Airlock2FAActivation)this.airlock2faActivation.get())
         .getBindingToken(Airlock2FAActivationCode.airlock2FAActivationCode(request.getActivationCode()));
      return this.mapper.toResponse(flowResult);
   }
}
