package com.airlock.iam.transactionapproval.infrastructure.resource.matrix;

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import ch.ergon.restal.jsonapi.document.data.ResourceObject;
import com.airlock.iam.common.application.jsonapi.ResourceIdentifierUtils;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.infrastructure.mapper.FlowResultMapper;
import com.airlock.iam.flow.shared.application.service.matrix.MatrixCheck;
import com.airlock.iam.flow.shared.domain.model.matrix.MatrixChallenge;
import com.airlock.iam.flow.shared.domain.model.matrix.MatrixChallengeResponse;
import com.airlock.iam.transactionapproval.api.infrastructure.generated.transfer.response.TransactionApprovalFlowResultDataResourceDocument;
import com.airlock.iam.transactionapproval.infrastructure.generated.transfer.response.TransactionApprovalMatrixChallengeDataResourceDocument;
import com.airlock.iam.transactionapproval.infrastructure.transfer.request.TransactionApprovalMatrixChallengeCheckRequest;
import com.airlock.iam.transactionapproval.infrastructure.transfer.response.TransactionApprovalMatrixChallengeData;
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.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import ma.glasnost.orika.MapperFacade;

@Path("/transaction-approval/matrix")
public class MatrixTransactionApprovalFlowResource {
   private final FlowResultMapper resultMapper;
   private final MapperFacade mapperFacade;
   private final Provider<MatrixCheck> matrixAuthentication;

   @Inject
   public MatrixTransactionApprovalFlowResource(FlowResultMapper resultMapper, MapperFacade mapperFacade, Provider<MatrixCheck> matrixAuthentication) {
      this.resultMapper = resultMapper;
      this.mapperFacade = mapperFacade;
      this.matrixAuthentication = matrixAuthentication;
   }

   @ApiResponse(
      responseCode = "200",
      description = "The challenge has been returned.",
      content = {
            @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = TransactionApprovalMatrixChallengeDataResourceDocument.class)),
            @Content(mediaType = "application/json", schema = @Schema(implementation = TransactionApprovalMatrixChallengeDataResourceDocument.class))
      }
   )
   @Deprecated
   @POST
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Path("/challenge/retrieve")
   @Operation(
      description = "> [!caution]\n> This endpoint is deprecated. The challenges are returned as\n<a href=\"#additionalTransactionApprovalAttributes\">additional attribute</a> with the <tt>MATRIX_RESPONSE_REQUIRED</tt> next step code.\n<p>Returns one or more challenges for matrix/index list transaction approval.</p>\n<div class=\"iam-resource-return\">A list of one or more matrix coordinates or index list indexes.</div>",
      tags = "Matrix Cards",
      summary = "Get challenges"
   )
   public Response retrieveChallenge() {
      FlowResult result = ((MatrixCheck)this.matrixAuthentication.get()).getCurrentChallenge();
      if (result.isFailed()) {
         return this.resultMapper.toResponse(result, new ResourceObject[0]);
      }

      MatrixChallenge currentChallenge = (MatrixChallenge)result.data().get();
      TransactionApprovalMatrixChallengeData attributes = (TransactionApprovalMatrixChallengeData)this.mapperFacade
         .map(currentChallenge, TransactionApprovalMatrixChallengeData.class);
      ResourceDocument<TransactionApprovalMatrixChallengeData> document = ResourceObject.builder(ResourceIdentifierUtils.getIdFor(attributes), attributes)
         .document();
      return Response.ok(document).build();
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Transaction approval successful or further steps required.",
               content = {
                     @Content(
                        mediaType = "application/vnd.api+json",
                        schema = @Schema(implementation = TransactionApprovalFlowResultDataResourceDocument.class)
                     ),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = TransactionApprovalFlowResultDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "400",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>MATRIX_RESPONSE_WRONG</td>\n<td>One or more responses could not be validated successfully. Retry again.<br>\nSince challenge coordinates may change after failed attempts (depending on configuration),\nthe new ones might have to be requested again.</td>\n</tr>\n</table>\n</div>\n<br>Possible next step: <tt>MATRIX_RESPONSE_REQUIRED</tt> (as documented <a href=\"#nextApprovalStepCodes\">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 = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>MATRIX_RESPONSE_WRONG</td>\n<td>One or more responses could not be validated successfully.</td>\n</tr>\n<tr>\n<td>TOO_MANY_UNANSWERED_CHALLENGES</td>\n<td>Too many challenges were requested but not answered. Try again later.</td>\n</tr>\n</table>\n</div>\n<br>The current transaction approval 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"))
               }
            )
      }
   )
   @POST
   @Path("/check")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Operation(
      description = "Verifies the submitted challenge response.\n<div class=\"iam-resource-return\">Whether the check was successful. If the attribute <tt>nextAuthStep</tt> is present in the response,\n further steps are required to successfully approve the transaction. If the attribute is missing, the transaction is\n successfully approved.</div>",
      tags = "Matrix Cards",
      summary = "Verify response"
   )
   public Response checkChallengeResponse(
      @Parameter(description = "Contains the challenge response(s).") @NotNull @Valid TransactionApprovalMatrixChallengeCheckRequest request
   ) {
      List<MatrixChallengeResponse> challengeResponses = new ArrayList<>();

      for (Entry<String, String> entry : request.getChallengeResponse().entrySet()) {
         challengeResponses.add(new MatrixChallengeResponse(entry.getKey(), entry.getValue()));
      }

      FlowResult result = ((MatrixCheck)this.matrixAuthentication.get()).checkChallengeResponse(challengeResponses);
      return this.resultMapper.toResponse(result, new ResourceObject[0]);
   }
}
