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

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import ch.ergon.restal.jsonapi.document.data.ResourceObject;
import com.airlock.iam.authentication.infrastructure.generated.transfer.response.LocationInterpretationDataResourceDocument;
import com.airlock.iam.authentication.infrastructure.transfer.request.LocationInterpretationRequest;
import com.airlock.iam.authentication.infrastructure.transfer.response.LocationInterpretationData;
import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.base.infrastructure.error.GenericRestErrorCode;
import com.airlock.iam.base.infrastructure.mapper.ValidationToErrorMapper;
import com.airlock.iam.common.application.jsonapi.ResourceIdentifierUtils;
import com.airlock.iam.login.application.service.location.LocationService;
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 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 jakarta.ws.rs.core.Response.Status;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
import ma.glasnost.orika.MapperFacade;

@Path("/location/interpret")
public class LocationInterpretationResource {
   private final Provider<LocationService> locationService;
   private final MapperFacade mapperFacade;

   @Inject
   public LocationInterpretationResource(MapperFacade mapperFacade, Provider<LocationService> locationService) {
      this.mapperFacade = mapperFacade;
      this.locationService = locationService;
   }

   @ApiResponse(
      responseCode = "200",
      description = "An interpretation for the given URI has been returned.\n<p>The set of interpretations is empty if no location interpreters are configured.</p>\n",
      content = {
            @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = LocationInterpretationDataResourceDocument.class)),
            @Content(mediaType = "application/json", schema = @Schema(implementation = LocationInterpretationDataResourceDocument.class))
      }
   )
   @Operation(
      description = "Interprets a forward location URI prior to starting an authentication flow.\n<div class=\"iam-resource-return\">A map of key/values that have been extracted or derived from the given URI.</div>",
      tags = "Location",
      summary = "Interpret URI"
   )
   @POST
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response interpret(
      @Parameter(description = "Contains the forward location URI to be interpreted.") @NotNull @Valid LocationInterpretationRequest request
   ) {
      Optional<URI> uri = createURI(request.getLocation());
      if (uri.isPresent()) {
         LocationInterpretationData data = (LocationInterpretationData)this.mapperFacade
            .map(((LocationService)this.locationService.get()).interpret(uri.get()), LocationInterpretationData.class);
         ResourceDocument<LocationInterpretationData> document = ResourceObject.builder(ResourceIdentifierUtils.getIdFor(data), data).document();
         return Response.status(Status.OK).entity(document).build();
      } else {
         return ErrorResponse.badRequest()
            .error(ValidationToErrorMapper.createErrorData(GenericRestErrorCode.VALIDATION_FAILED, GenericError.INVALID_VALUE, "/location"))
            .log(String.format("The location '%s' is not a valid URI", request.getLocation()))
            .build()
            .toResponse();
      }
   }

   private static Optional<URI> createURI(String uri) {
      try {
         return Optional.of(new URI(uri));
      } catch (URISyntaxException e) {
         return Optional.empty();
      }
   }
}
