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

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import com.airlock.iam.apipolicyservice.application.service.ResolveTechClientService;
import com.airlock.iam.apipolicyservice.domain.model.ResolveTechClientError;
import com.airlock.iam.apipolicyservice.domain.model.ResolveTechClientResult;
import com.airlock.iam.apipolicyservice.infrastructure.generated.transfer.response.ApiPolicyServiceTechClientDataResourceDocument;
import com.airlock.iam.apipolicyservice.infrastructure.transfer.request.ResolveTechClientRequest;
import com.airlock.iam.apipolicyservice.infrastructure.transfer.response.ApiPolicyServiceTechClientData;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.base.infrastructure.mapper.entity.RestMapper;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.infrastructure.annotation.DisableResponseDelay;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
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.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;

@Path("resolve-tech-client")
public class ResolveTechClientResource {
   private static final ELogger LOG = ELoggerFactory.getLogger(ResolveTechClientResource.class);
   private final Provider<ResolveTechClientService> resolveTechClientService;
   private final RestMapper restMapper;

   @Inject
   public ResolveTechClientResource(Provider<ResolveTechClientService> resolveTechClientService, RestMapper restMapper) {
      this.resolveTechClientService = resolveTechClientService;
      this.restMapper = restMapper;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Technical client information could be retrieved for the API key.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = ApiPolicyServiceTechClientDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = ApiPolicyServiceTechClientDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "400",
               description = "<div class=\"iam-error-code\"><table>\n<tr>\n<td>API_KEY_NOT_FOUND</td>\n<td>The API key could not be found.</td>\n</tr>\n<tr>\n<td>API_KEY_INVALID</td>\n<td>The API key is not yet valid or already expired.</td>\n</tr>\n<tr>\n<td>API_KEY_LOCKED</td>\n<td>The API key is locked.</td>\n</tr>\n<tr>\n<td>TECH_CLIENT_LOCKED</td>\n<td>The technical client associated with the given API key is locked.</td>\n</tr>\n</table>\n</div>\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
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @DisableResponseDelay
   @Operation(
      summary = "Resolve technical client",
      tags = "Technical Clients",
      description = "Resolves a technical client for an API key.\n<div class=\"iam-resource-return\">Response containing the information about the technical client that corresponds to the API key.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true"))
   )
   public Response resolveTechClient(@Parameter(description = "Contains the API key.") @NotNull @Valid ResolveTechClientRequest request) {
      LOG.debug("Trying to resolve technical client for API key " + LogUtil.logSafeQuote(removeSensitiveData(request.getApiKey())));
      ResolveTechClientResult result = ((ResolveTechClientService)this.resolveTechClientService.get()).resolveTechClient(request.getApiKey());
      if (result.getTechClient().isPresent()) {
         ResourceDocument<ApiPolicyServiceTechClientData> document = this.restMapper
            .forElement(result.getTechClient().get(), ApiPolicyServiceTechClientData.class)
            .withIdSupplier(t -> t.getId().asString())
            .toResourceDocument();
         LOG.debug("Resolved API key to technical client: " + result.getTechClient().get().getId() + " (" + result.getTechClient().get().getDisplayName() + ")");
         return Response.ok(document).build();
      } else {
         LOG.info(
            "Failed to resolve technical client for API key "
               + LogUtil.logSafeQuote(removeSensitiveData(request.getApiKey()))
               + " with error: "
               + result.getError().orElseThrow().getMessage()
         );
         return toErrorResponse(result.getError().get());
      }
   }

   private static Response toErrorResponse(ResolveTechClientError status) {
      return ErrorResponse.badRequest().errorCode(status::name).build().toResponse();
   }

   private static String removeSensitiveData(String apiKey) {
      return ".." + StringUtils.right(apiKey, 8);
   }
}
