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

import com.airlock.iam.authentication.infrastructure.generated.transfer.response.UserDataEditItemInfoAuthResponseAttributesDataCollectionDocument;
import com.airlock.iam.authentication.infrastructure.transfer.request.UserDataEditAuthRequest;
import com.airlock.iam.authentication.infrastructure.transfer.response.UserDataEditItemInfoAuthResponseAttributesData;
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.FlowResultToCollectionDocumentMapper;
import com.airlock.iam.flow.infrastructure.mapper.ValidationResultMapper;
import com.airlock.iam.flow.shared.api.domain.model.item.info.UserDataItemInfo;
import com.airlock.iam.flow.shared.application.service.data.UserDataEdit;
import com.airlock.iam.flow.shared.domain.model.item.UserDataItemsInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
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 ma.glasnost.orika.MapperFacade;

@Path("/data")
public class UserDataEditAuthFlowResource {
   private final FlowResultMapper mapper;
   private final MapperFacade mapperFacade;
   private final Provider<UserDataEdit> edit;
   private final Provider<ValidationResultMapper> stepErrorResultMapper;

   @Inject
   public UserDataEditAuthFlowResource(
      FlowResultMapper mapper, MapperFacade mapperFacade, Provider<UserDataEdit> edit, Provider<ValidationResultMapper> stepErrorResultMapper
   ) {
      this.mapper = mapper;
      this.mapperFacade = mapperFacade;
      this.edit = edit;
      this.stepErrorResultMapper = stepErrorResultMapper;
   }

   @ApiResponse(
      responseCode = "200",
      description = "The information has successfully been returned.",
      content = {
            @Content(
               mediaType = "application/vnd.api+json",
               schema = @Schema(implementation = UserDataEditItemInfoAuthResponseAttributesDataCollectionDocument.class)
            ),
            @Content(mediaType = "application/json", schema = @Schema(implementation = UserDataEditItemInfoAuthResponseAttributesDataCollectionDocument.class))
      }
   )
   @Operation(
      description = "Retrieves information about context data items to be edited.\n<div class=\"iam-resource-return\">A list containing information for each context-data item that can be edited in the current step.</div>",
      tags = "User Context Data",
      summary = "Get info"
   )
   @Path("info/retrieve")
   @POST
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response retrieveInfo() {
      FlowResult result = ((UserDataEdit)this.edit.get()).retrieveEditInfo();
      return FlowResultToCollectionDocumentMapper.flowResultDataToCollectionDocumentMapper(this.mapper, this.mapperFacade)
         .map(result, UserDataItemsInfo.class, UserDataEditItemInfoAuthResponseAttributesData.class, UserDataItemInfo::getName);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Data items successfully set. The <tt>nextStep</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 steps: <tt>USER_DATA_EDIT_REQUIRED</tt> or <tt>USER_DATA_EDIT_POSSIBLE</tt> (as documented\n<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 authentication 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 context-data items for a user.\n<p>This endpoint updates only items sent with the request. Omitted items remain unchanged.\nContext-data items with a value of <tt>null</tt> are deleted. </p>\n<div class=\"iam-resource-return\">Whether the data items were accepted and set. If a request contains both valid and invalid items, the valid items will be processed.\n This endpoint returns a <tt>nextStep</tt> with\n value <tt>USER_DATA_EDIT_REQUIRED</tt> if the step's data is not yet valid. It returns a <tt>nextStep</tt> with\n value <tt>USER_DATA_EDIT_POSSIBLE</tt> if the step's data is valid and a follow-up call to the <tt>continue</tt> endpoint\n will advance the flow.\n <p>\n On error, this endpoint returns a detailed list of validation failures.\n </p></div>",
      tags = "User Context Data",
      summary = "Edit items"
   )
   @Path("edit")
   @POST
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response editData(
      @NotNull @Valid @RequestBody(
   description = "<div class=\"iam-docu\">\nContains the data items to be edited. The format of an individual data item's value depends on its configured type:\n<table>\n<tr><th>Item Type</th><th>Format</th></tr>\n<tr><td><i>Boolean</i></td><td>One of the literals <tt>true</tt> or <tt>false</tt></td></tr>\n<tr><td><i>String</i></td><td>A double-quoted string</td></tr>\n<tr><td><i>Date</i></td><td>The format <tt>full-date</tt> as defined in <a href=\"#dateAndTimeFormat\">Date and time format</a> (e.g. <tt>2018-02-06</tt>).</td></tr>\n<tr><td><i>Date And Time</i></td><td>The format <tt>date-time</tt> as defined in <a href=\"#dateAndTimeFormat\">Date and time format</a> (e.g. <tt>2018-02-06T15:58:53.661Z</tt>)</td></tr>\n</table>\n</div>",
   content = @Content(
      mediaType = "application/json",
      examples = {@ExampleObject(name = "Set username", value = "{\"username\":\"alice\"}"), @ExampleObject(name = "Remove email", value = "{\"email\":null}")}
   )
) UserDataEditAuthRequest request
   ) {
      FlowResult result = ((UserDataEdit)this.edit.get()).editData(request.getData());
      return ((ValidationResultMapper)this.stepErrorResultMapper.get()).toValidationResponse(result);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "All data successfully set. A call to the <tt>continue</tt> endpoint will advance the flow.",
               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 steps: <tt>USER_DATA_EDIT_REQUIRED</tt> or <tt>USER_DATA_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 authentication 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 context-data edit step.\n<div class=\"iam-resource-return\">Whether the data validated successfully. On success, this endpoint returns a <tt>nextStep</tt> with\n value <tt>USER_DATA_EDIT_POSSIBLE</tt>.\n <p>\n On error, this endpoint returns a detailed list of validation failures.\n </p></div>",
      tags = "User Context Data",
      summary = "Validate data"
   )
   @Path("validate")
   @POST
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response validate() {
      FlowResult result = ((UserDataEdit)this.edit.get()).validate();
      return ((ValidationResultMapper)this.stepErrorResultMapper.get()).toValidationResponse(result);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "All data successfully received and authentication 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 steps: <tt>USER_DATA_EDIT_REQUIRED</tt> or <tt>USER_DATA_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 authentication 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 context-data edit step and continue to the next step if possible.\n<div class=\"iam-resource-return\">If the attribute <tt>nextStep</tt> is\n present in the response, further steps are required to successfully edit the context-data. If the attribute is missing, the\n user has successfully completed the authentication flow.\n <p>\n On error, this endpoint returns a detailed list of validation failures.\n </p></div>",
      tags = "User Context Data",
      summary = "Validate and continue"
   )
   @Path("continue")
   @POST
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response validateAndContinue() {
      FlowResult result = ((UserDataEdit)this.edit.get()).validateAndContinue();
      return ((ValidationResultMapper)this.stepErrorResultMapper.get()).toValidationResponse(result);
   }
}
