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

import ch.ergon.restal.jsonapi.document.data.ResourceObject;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.api.infrastructure.mapper.FlowResultMapper;
import com.airlock.iam.transactionapproval.api.infrastructure.generated.transfer.response.TransactionApprovalFlowResultDataResourceDocument;
import com.airlock.iam.transactionapproval.application.service.TransactionApprovalParameterHandler;
import com.airlock.iam.transactionapproval.infrastructure.transfer.request.TransactionApprovalParameterRequest;
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 java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.stream.Collectors;

@Path("/transaction-approval/parameters")
public class TransactionApprovalParameterFlowResource {
   private static final String TA_APPROVAL_PRAM_REQ_EXAMPLE = "{\n\t\"authTokenId\": \"12345\",\n\t\"messageParameters\": {\n\t\t\"accountNumber\": \"12345\",\n\t\t\"amount\": \"300\",\n\t\t\"currency\": \"CHF\"\n\t}\n}\n";
   private final FlowResultMapper mapper;
   private final Provider<TransactionApprovalParameterHandler> transactionApprovalParameterHandler;

   @Inject
   public TransactionApprovalParameterFlowResource(FlowResultMapper mapper, Provider<TransactionApprovalParameterHandler> transactionApprovalParameterHandler) {
      this.mapper = mapper;
      this.transactionApprovalParameterHandler = transactionApprovalParameterHandler;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Message parameters and id for device selection successfully validated and saved.",
               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 = "If some parameters are missing or not supported.\n<br>Possible next step: <tt>PARAMETERS_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 = "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"))
               }
            )
      }
   )
   @POST
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Operation(
      description = "Validate and save parameters for transaction approval.\nValidates and saves transaction approval message parameters and accepts an \"Auth Token ID\"\nthat identifies the device used for login.\nIn contrast to other endpoints, <tt>null</tt> values are ignored here (no \"delete\" semantic).\n<div class=\"iam-resource-return\">Whether validation of the message parameters was successful. If the attribute <tt>nextStep</tt> is present in the response,\n further steps are required to successfully approve the transaction. If the attribute is missing, the transaction approval\n of the user was successful.</div>",
      tags = "Message Parameters",
      summary = "Validate and save"
   )
   public Response validateAndConsume(
      @NotNull @Valid @RequestBody(
   description = "Contains the id used for device selection in further steps and message parameters used to create a transaction approval message.",
   content = @Content(
      mediaType = "application/json",
      examples = @ExampleObject(
         name = "Save transaction approval message parameters",
         value = "{\n\t\"authTokenId\": \"12345\",\n\t\"messageParameters\": {\n\t\t\"accountNumber\": \"12345\",\n\t\t\"amount\": \"300\",\n\t\t\"currency\": \"CHF\"\n\t}\n}\n"
      )
   )
) TransactionApprovalParameterRequest request
   ) {
      Map<String, String> messageParameters = filterNullValues(request.getMessageParameters());
      Optional<String> authTokenId = Optional.ofNullable(request.getAuthTokenId());
      FlowResult result = ((TransactionApprovalParameterHandler)this.transactionApprovalParameterHandler.get())
         .validateAndConsume(messageParameters, authTokenId);
      return this.mapper.toResponse(result, new ResourceObject[0]);
   }

   private static Map<String, String> filterNullValues(Map<String, String> map) {
      return map.entrySet().stream().filter(e -> e.getValue() != null).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
   }
}
