package com.airlock.iam.admin.infrastructure.resource.prot.techclient;

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import ch.ergon.restal.jsonapi.document.request.RequestDocument;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.techclient.PlanUsageDataResourceDocument;
import com.airlock.iam.admin.infrastructure.transfer.request.techclient.PlanUsageUpdateRequest;
import com.airlock.iam.admin.infrastructure.transfer.response.techclient.PlanUsageData;
import com.airlock.iam.base.api.domain.model.exception.EntityNotUniqueException;
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.base.infrastructure.mapper.ValidationToErrorMapper;
import com.airlock.iam.base.infrastructure.mapper.entity.RestMapper;
import com.airlock.iam.common.infrastructure.validation.JsonApiUpdateResourceIdValidator;
import com.airlock.iam.techclient.admin.application.service.PlanUsageService;
import com.airlock.iam.techclient.admin.domain.model.PlanUsage;
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.DELETE;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import java.util.Optional;

@Path("/plan-usages/{id}")
public class PlanUsageResource {
   private final Provider<PlanUsageService> planUsageService;
   private final JsonApiUpdateResourceIdValidator idValidator;
   private final RestMapper restMapper;
   private final ValidationToErrorMapper errorMapper;

   @Inject
   public PlanUsageResource(
      Provider<PlanUsageService> planUsageService, JsonApiUpdateResourceIdValidator idValidator, RestMapper restMapper, ValidationToErrorMapper errorMapper
   ) {
      this.planUsageService = planUsageService;
      this.idValidator = idValidator;
      this.restMapper = restMapper;
      this.errorMapper = errorMapper;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "The Plan-usage was updated.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = PlanUsageDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = PlanUsageDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "404",
               description = "The Plan-usage does not exist.",
               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 = "409",
               description = "A Plan-usage with the given name does already exist for the associated subscription.",
               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"))
               }
            )
      }
   )
   @PATCH
   @Operation(
      description = "Updates the Plan-usage.\n<div class=\"iam-resource-return\">The updated Plan-usage.</div>",
      tags = "Technical Clients",
      summary = "Update plan usage"
   )
   @Consumes({"application/vnd.api+json", "application/json"})
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response updatePlanUsage(
      @Parameter(description = "The ID of the Plan-usage.") @PathParam("id") String planUsageId,
      @Parameter(description = "The request containing Plan-usage attributes. Omitted attributes are left unchanged.") @NotNull @Valid RequestDocument<PlanUsageUpdateRequest> requestDocument
   ) {
      Optional<Response> errorResponse = this.idValidator.validate(planUsageId, requestDocument);
      if (errorResponse.isPresent()) {
         return errorResponse.get();
      }

      Optional<PlanUsage> planUsage = this.planUsageService().findPlanUsage(PlanUsage.planUsageId(planUsageId));
      if (planUsage.isEmpty()) {
         return ErrorResponse.notFound().build().toResponse();
      }

      PlanUsageUpdateRequest request = (PlanUsageUpdateRequest)requestDocument.getDataAttributes();
      if (request == null) {
         return this.planUsageResponseDocument(planUsage.get());
      }

      PlanUsage updatedPlanUsage = this.applyChangedValues(request, planUsage.get());

      try {
         updatedPlanUsage = this.planUsageService().updatePlanUsage(updatedPlanUsage);
      } catch (EntityNotUniqueException e) {
         return this.errorMapper.mapToResponse(ValidationResult.singleError("name", GenericError.NOT_UNIQUE).getErrors());
      }

      return this.planUsageResponseDocument(updatedPlanUsage);
   }

   private PlanUsage applyChangedValues(PlanUsageUpdateRequest request, PlanUsage planUsage) {
      request.getName().ifPresent(planUsage::setName);
      request.getRateLimit().ifPresent(planUsage::setRateLimit);
      return planUsage;
   }

   private Response planUsageResponseDocument(PlanUsage planUsage) {
      ResourceDocument<PlanUsageData> document = this.restMapper.forEntity(planUsage, PlanUsageData.class).toResourceDocument();
      return Response.ok().entity(document).build();
   }

   @ApiResponses(
      {
            @ApiResponse(responseCode = "204", description = "The Plan-Usage has been deleted."),
            @ApiResponse(
               responseCode = "404",
               description = "The Plan-Usage does not exist.",
               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"))
               }
            )
      }
   )
   @DELETE
   @Operation(
      description = "Deletes the specified Plan-Usage.\n<div class=\"iam-resource-return\">A Response indicating whether the Plan-Usage has been successfully deleted or not.</div>",
      tags = "Technical Clients",
      summary = "Delete plan usage"
   )
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response delete(@Parameter(description = "The identifier of the Plan-Usage.") @PathParam("id") String id) {
      if (this.planUsageService().findPlanUsage(PlanUsage.planUsageId(id)).isEmpty()) {
         return ErrorResponse.notFound().build().toResponse();
      }

      this.planUsageService().deletePlanUsage(PlanUsage.planUsageId(id));
      return Response.noContent().build();
   }

   private PlanUsageService planUsageService() {
      return (PlanUsageService)this.planUsageService.get();
   }
}
