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

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import ch.ergon.restal.jsonapi.document.data.ResourceObject;
import ch.ergon.restal.jsonapi.document.request.RequestDocument;
import com.airlock.iam.admin.application.service.user.UserValidityService;
import com.airlock.iam.admin.domain.model.user.UserSpecificSettings;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.user.UserValidityDataResourceDocument;
import com.airlock.iam.admin.infrastructure.mapper.prot.UserMapper;
import com.airlock.iam.admin.infrastructure.service.user.UserIdResolver;
import com.airlock.iam.admin.infrastructure.transfer.request.user.UserValidityPeriodRequest;
import com.airlock.iam.admin.infrastructure.transfer.response.user.UserValidityData;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.api.infrastructure.uri.ExternalUriInfo;
import com.airlock.iam.base.infrastructure.mapper.ValidationToErrorMapper;
import com.airlock.iam.common.api.domain.model.user.validity.UserValidity;
import com.airlock.iam.common.application.exception.RequestDocumentAttributesMissingException;
import com.airlock.iam.common.infrastructure.resource.Resources;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
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.GET;
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;

@Path("/users/{userId}/validity")
public class UserValidityResource {
   private final Provider<UserValidityService> validityService;
   private final UserMapper mapper;
   private final Provider<UserSpecificSettings> settings;
   private final Provider<ExternalUriInfo> uriInfo;
   private final HelpdeskLogger helpdeskLogger;
   private final ValidationToErrorMapper errorMapper;
   private final Provider<UserIdResolver> userIdResolver;

   @Inject
   public UserValidityResource(
      Provider<UserValidityService> userValidityServiceProvider,
      Provider<UserSpecificSettings> settings,
      Provider<ExternalUriInfo> uriInfo,
      HelpdeskLogger logger,
      ValidationToErrorMapper errorMapper,
      Provider<UserIdResolver> userIdResolver
   ) {
      this.settings = settings;
      this.uriInfo = uriInfo;
      this.errorMapper = errorMapper;
      this.userIdResolver = userIdResolver;
      this.mapper = new UserMapper();
      this.validityService = userValidityServiceProvider;
      this.helpdeskLogger = logger;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "The response contains a representation of the user's validity.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = UserValidityDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = UserValidityDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "404",
               description = "The user 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"))
               }
            )
      }
   )
   @GET
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Operation(
      description = "Retrieves information about the validity of a user account.\n<div class=\"iam-resource-return\">Information about the validity of the user's account.</div>",
      tags = "User Validity",
      summary = "Check account validity"
   )
   public Response retrieve(@PathParam("userId") @Parameter(schema = @Schema(type = "string")) String userIdParam) {
      UserId userId = ((UserIdResolver)this.userIdResolver.get()).resolve(userIdParam);
      UserValidity userValidity = this.validityService().getUserValidity(userId.getValue());
      UserValidityData data = this.mapper.mapUserValidity(userValidity);
      ResourceDocument<UserValidityData> document = ResourceObject.builder(userId.getValue(), data)
         .linkSelf(Resources.uri((ExternalUriInfo)this.uriInfo.get(), UserValidityResource.class).build(new Object[]{userId}))
         .document();
      return Response.ok(document).build();
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "The response contains the updated validity period.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = UserValidityDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = UserValidityDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "403",
               description = "The user is not editable.",
               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 user 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"))
               }
            )
      }
   )
   @PATCH
   @Consumes({"application/vnd.api+json", "application/json"})
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Operation(
      description = "Updates the validity period of this user.\n<div class=\"iam-resource-return\">Information about the validity of the user's account.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "User Validity",
      summary = "Update validity period"
   )
   public Response updateValidityPeriod(
      @PathParam("userId") @Parameter(schema = @Schema(type = "string")) String userIdParam,
      @Parameter(description = "The jsonapi document containing the new validity period.") @NotNull @Valid RequestDocument<UserValidityPeriodRequest> document
   ) {
      UserId userId = ((UserIdResolver)this.userIdResolver.get()).resolve(userIdParam);
      ((UserSpecificSettings)this.settings.get()).failIfUserNotEditable();
      UserValidityPeriodRequest attributes = (UserValidityPeriodRequest)RequestDocumentAttributesMissingException.checkAttributesPresent(document);
      ValidationResult validation = this.validityService().validate(attributes.getFrom(), attributes.getUntil());
      if (!validation.isValid()) {
         return this.errorMapper.withJsonApiAttributesMapper().mapToResponse(validation.getErrors());
      }

      this.validityService().setNullableValidityPeriod(userId.getValue(), attributes.getFrom(), attributes.getUntil());
      UserValidity userValidity = ((UserValidityService)this.validityService.get()).getUserValidity(userId.getValue());
      this.helpdeskLogger.log(userId, "Administrator ${authentee} updated validity of user ${affectedUser}. " + userValidity.getDescription());
      ResourceDocument<UserValidityData> responseDocument = ResourceObject.builder(userId.getValue(), this.mapper.mapUserValidity(userValidity))
         .linkSelf(Resources.uri((ExternalUriInfo)this.uriInfo.get(), UserValidityResource.class).build(new Object[]{userId}))
         .document();
      return Response.ok().entity(responseDocument).build();
   }

   private UserValidityService validityService() {
      return (UserValidityService)this.validityService.get();
   }
}
