package com.airlock.iam.login.rest.infrastructure.resource.prot;

import com.airlock.iam.authentication.domain.service.secretquestions.SecretQuestionsLoginService;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.common.domain.model.token.secretquestions.SecretQuestionAnswer;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.login.rest.infrastructure.logger.ProtectedHelpdeskLogger;
import com.airlock.iam.login.rest.infrastructure.transfer.shared.request.AcceptSecretQuestionAnswerRequest;
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.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import java.security.Principal;

@Path("/my/secret-questions")
public class UserSecretQuestionsResource {
   private static final ELogger LOG = ELoggerFactory.getLogger(UserSecretQuestionsResource.class);
   private final Provider<SecretQuestionsLoginService> secretQuestionsServiceProvider;
   private final ProtectedHelpdeskLogger helpdeskLogger;
   private final Provider<Principal> principalProvider;

   @Inject
   public UserSecretQuestionsResource(
      Provider<SecretQuestionsLoginService> serviceProvider, ProtectedHelpdeskLogger logger, Provider<Principal> principalProvider
   ) {
      this.secretQuestionsServiceProvider = serviceProvider;
      this.helpdeskLogger = logger;
      this.principalProvider = principalProvider;
   }

   @ApiResponses(
      {
            @ApiResponse(responseCode = "204", description = "The question has been answered."),
            @ApiResponse(
               responseCode = "400",
               description = "If another question already has the same answer but this is forbidden in the configuration.",
               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 = "Invalid or missing authentication.",
               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 the requested service is forbidden. Authentication will not help.",
               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 = "The question ID or the user do not exist or cannot be retrieved with the current configuration.",
               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 = "Allows the user to set the answer for a single Secret Question.\n<div class=\"iam-resource-return\">a Response containing a status code.</div>",
      tags = "Secret Questions",
      summary = "Set answer"
   )
   @POST
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Path("{questionId}/answer")
   public Response answerQuestion(
      @Parameter(description = "The question ID of the Secret Question whose answer should be set.") @PathParam("questionId") String questionId,
      @Parameter(description = "Contains the question's answer.") @NotNull @Valid AcceptSecretQuestionAnswerRequest request
   ) {
      SecretQuestionAnswer answer = this.getAnswerFromRequest(this.authenticatedUser(), questionId, request);
      ValidationResult validation = this.getService().validateAnswer(answer);
      if (validation.isValid()) {
         this.getService().setAnswer(answer);
         this.helpdeskLogger.log("User set a new answer for secret question '%s'.", questionId);
         return Response.noContent().build();
      }

      this.helpdeskLogger.log("User failed to set a new answer for secret question '%s': %s", questionId, validation.getFailure());
      if (validation.contains(GenericError.CONFLICT)) {
         LOG.info("The answer is a duplicate of another answer");
         return ErrorResponse.badRequest().build().toResponse();
      }

      if (validation.contains(GenericError.NOT_FOUND)) {
         return ErrorResponse.notFound().build().toResponse();
      }

      LOG.info("The answer for this question ID could not be set");
      return ErrorResponse.badRequest().build().toResponse();
   }

   private SecretQuestionAnswer getAnswerFromRequest(UserId userId, String questionId, AcceptSecretQuestionAnswerRequest request) {
      return SecretQuestionAnswer.builder().userId(userId.getValue()).answer(request.getAnswer()).questionResourceKey(questionId).build();
   }

   private SecretQuestionsLoginService getService() {
      return (SecretQuestionsLoginService)this.secretQuestionsServiceProvider.get();
   }

   private UserId authenticatedUser() {
      return UserId.userId(((Principal)this.principalProvider.get()).getName());
   }
}
