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

import ch.ergon.restal.jsonapi.document.error.ErrorData;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.infrastructure.mapper.ValidationToErrorMapper;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.infrastructure.mapper.FlowResultMapper;
import com.airlock.iam.flow.domain.model.ValidationStepResultData;
import com.airlock.iam.flow.shared.application.service.identification.UserIdentification;
import com.airlock.iam.flow.shared.infrastructure.mapper.UserIdentificationFieldMapper;
import com.airlock.iam.publicselfservice.api.infrastructure.generated.transfer.response.PublicSelfServiceFlowResultDataResourceDocument;
import com.airlock.iam.publicselfservice.infrastructure.transfer.request.IdentifyUserRequest;
import com.airlock.iam.publicselfservice.infrastructure.transfer.request.UserIdentificationRequest;
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.List;
import java.util.Map;

@Path("/")
public class PublicSelfServiceUserIdentificationResource {
   private static final UserIdentificationFieldMapper IDENTIFICATION_MAPPER = new UserIdentificationFieldMapper();
   private final Provider<UserIdentification> userIdentification;
   private final Provider<FlowResultMapper> flowResultMapper;
   private final Provider<ValidationToErrorMapper> errorMapper;

   @Inject
   public PublicSelfServiceUserIdentificationResource(
      Provider<UserIdentification> identification, Provider<FlowResultMapper> flowResultMapper, Provider<ValidationToErrorMapper> errorMapper
   ) {
      this.userIdentification = identification;
      this.flowResultMapper = flowResultMapper;
      this.errorMapper = errorMapper;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "A user that is eligible for a public self-service has been successfully identified. Further steps\nrequired. Note that this may also be returned when a public self-service restriction is violated if feedback for said\nrestriction is disabled.\n",
               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>USER_NOT_FOUND</td>\n<td>The specified user could not be found.</td>\n</tr>\n<tr>\n<td>USER_INVALID</td>\n<td>The specified user is invalid.</td>\n</tr>\n<tr>\n<td>USER_LOCKED</td>\n<td>The specified user is locked.</td>\n</tr>\n<tr>\n<td>CAPTCHA_CHECK_FAILED</td>\n<td>The CAPTCHA could not be validated successfully. Retry with a correctly solved CAPTCHA.</td>\n</tr>\n</table>\n</div>\n<br>The current public self-service 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"))
               }
            )
      }
   )
   @Deprecated
   @Operation(
      description = "> [!caution]\n> This endpoint is deprecated. Use <tt>/public/self-service/user/identify/</tt> instead.\n\n> [!important]\n> <strong>CAPTCHA</strong>: When protection is enabled, a challenge is included in the previous response.\n<p>Identifies the user.</p>\n<div class=\"iam-resource-return\">Whether the user has been successfully identified. The attribute <tt>nextStep</tt>\n in the response indicates the next step required for the public self-service.</div>",
      tags = "User",
      summary = "Identify by username"
   )
   @POST
   @Path("username/identify")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response identifyUserDeprecated(@Parameter(description = "Contains the username.") @NotNull @Valid IdentifyUserRequest request) {
      return this.identifyUser(request.getUsername(), null);
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "A user that is eligible for a public self-service has been successfully identified. Further steps\nrequired. Note that this may also be returned when a public self-service restriction is violated if feedback for said\nrestriction is disabled.\n",
               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>VALIDATION_FAILED</td>\n<td>The provided username is invalid or the user data could not be validated successfully by at least one of the context data item validators.\nSee <a href=\"#validationFailures\">validation failures</a> for detail codes.</td>\n</tr>\n</table>\n</div>\n<br>Possible next step: USERNAME_REQUIRED or USER_DATA_REQUIRED.\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>USER_NOT_FOUND</td>\n<td>The specified user could not be found.</td>\n</tr>\n<tr>\n<td>USER_INVALID</td>\n<td>The specified user is invalid.</td>\n</tr>\n<tr>\n<td>USER_LOCKED</td>\n<td>The specified user is locked.</td>\n</tr>\n<tr>\n<td>CAPTCHA_CHECK_FAILED</td>\n<td>The CAPTCHA could not be validated successfully. Retry with a correctly solved CAPTCHA.</td>\n</tr>\n</table>\n</div>\n<br>The current public self-service 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 = "> [!important]\n> <strong>CAPTCHA</strong>: When protection is enabled, a challenge is included in the previous response.\n<p>Identifies the user based on their username or the provided user data items.</p>\n<div class=\"iam-resource-return\">Whether the user has been successfully identified. The attribute <tt>nextStep</tt>\n in the response indicates the next step required for the public self-service.</div>",
      tags = "User",
      summary = "Identify user"
   )
   @POST
   @Path("user/identify")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response identifyUser(@Parameter(description = "Contains the username.") @NotNull @Valid UserIdentificationRequest request) {
      return this.identifyUser(request.getUsername(), request.getData());
   }

   private Response identifyUser(String username, Map<String, String> userData) {
      FlowResult flowResult = ((UserIdentification)this.userIdentification.get()).identifyUser(username, userData);
      return this.mapToResponse(flowResult);
   }

   private Response mapToResponse(FlowResult flowResult) {
      ValidationResult validationResult = flowResult.data(ValidationStepResultData.class)
         .map(ValidationStepResultData::result)
         .orElse(ValidationResult.valid());
      if (validationResult.isNotValid()) {
         List<ErrorData> errorData = ((ValidationToErrorMapper)this.errorMapper.get())
            .withFieldToSourcePointerMapper(IDENTIFICATION_MAPPER)
            .toErrorData(validationResult.getErrors());
         return ((FlowResultMapper)this.flowResultMapper.get()).toResponse(flowResult, errorData);
      } else {
         return ((FlowResultMapper)this.flowResultMapper.get()).toResponse(flowResult);
      }
   }
}
