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

import ch.ergon.restal.jsonapi.document.data.CollectionDocument;
import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import ch.ergon.restal.jsonapi.document.request.RequestDocument;
import ch.ergon.restal.jsonapi.parameter.Pager;
import com.airlock.iam.admin.api.domain.model.administrator.CurrentAdministrator;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.techclient.PlainTechClientDataCollectionDocument;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.techclient.PlainTechClientDataResourceDocument;
import com.airlock.iam.admin.infrastructure.mapper.prot.TechClientSearch;
import com.airlock.iam.admin.infrastructure.transfer.request.techclient.PlainTechClientCreateRequest;
import com.airlock.iam.admin.infrastructure.transfer.response.techclient.PlainTechClientData;
import com.airlock.iam.base.api.infrastructure.uri.ExternalUriInfo;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.base.infrastructure.mapper.entity.RestMapper;
import com.airlock.iam.base.infrastructure.mapper.jsonapi.JsonApiCollectionMapper;
import com.airlock.iam.base.infrastructure.validation.DocumentedFilterParameter;
import com.airlock.iam.base.infrastructure.validation.DocumentedSortParameter;
import com.airlock.iam.base.infrastructure.validation.SanitizedPageParameter;
import com.airlock.iam.base.infrastructure.validation.ValidPageParameter;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.application.exception.RequestDocumentAttributesMissingException;
import com.airlock.iam.common.infrastructure.resource.Resources;
import com.airlock.iam.log.api.signing.SigningLogger;
import com.airlock.iam.techclient.admin.application.service.PlainTechClientAttributes;
import com.airlock.iam.techclient.admin.application.service.PlainTechClientService;
import com.airlock.iam.techclient.admin.domain.model.PlainTechClient;
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.BeanParam;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
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.UriBuilder;
import java.net.URI;
import java.util.Collection;
import java.util.stream.Stream;

@Path("/tech-clients")
public class TechClientsResource {
   private final RestMapper mapper;
   private final Provider<PlainTechClientService> plainTechClientService;
   private final Provider<CurrentAdministrator> currentAdministrator;
   private final SigningLogger signingLogger;
   private final Provider<ExternalUriInfo> uriInfo;

   @Inject
   public TechClientsResource(
      RestMapper mapper,
      Provider<PlainTechClientService> plainTechClientService,
      Provider<CurrentAdministrator> currentAdministrator,
      SigningLogger signingLogger,
      Provider<ExternalUriInfo> uriInfo
   ) {
      this.mapper = mapper;
      this.plainTechClientService = plainTechClientService;
      this.currentAdministrator = currentAdministrator;
      this.signingLogger = signingLogger;
      this.uriInfo = uriInfo;
   }

   @ApiResponse(
      responseCode = "201",
      description = "The technical client was created.",
      content = {
            @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = PlainTechClientDataResourceDocument.class)),
            @Content(mediaType = "application/json", schema = @Schema(implementation = PlainTechClientDataResourceDocument.class))
      }
   )
   @POST
   @Operation(
      description = "Creates a new technical client.\n<div class=\"iam-resource-return\">A resource document describing the created technical client.</div>",
      tags = "Technical Clients",
      summary = "Create technical client"
   )
   @Consumes({"application/vnd.api+json", "application/json"})
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response createClient(
      @Parameter(description = "The attributes required to create a technical client.") @NotNull @Valid RequestDocument<PlainTechClientCreateRequest> requestDocument
   ) {
      if (requestDocument.getData().getId() != null) {
         return ErrorResponse.forbidden().log("Cannot accept client-generated ID.").build().toResponse();
      }

      PlainTechClientCreateRequest requestAttributes = (PlainTechClientCreateRequest)RequestDocumentAttributesMissingException.checkAttributesPresent(
         requestDocument
      );
      PlainTechClientAttributes attributes = toPlainTechClientAttributes(requestAttributes);
      PlainTechClient client = this.plainTechClientService().insert(attributes);
      this.signingLogger
         .log(
            "Administrator %s created a technical client (ID: %s) with display name %s",
            new Object[]{LogUtil.quote(this.currentAdmin()), LogUtil.quote(client.getId()), LogUtil.quote(client.getDisplayName())}
         );
      URI clientUri = Resources.uri((ExternalUriInfo)this.uriInfo.get(), TechClientsResource.class).path("{id}").build(new Object[]{client.getId().asString()});
      ResourceDocument<PlainTechClientData> document = TechClientResource.toTechClientDocument(client, this.mapper);
      return Response.created(clientUri).entity(document).build();
   }

   private static PlainTechClientAttributes toPlainTechClientAttributes(PlainTechClientCreateRequest requestAttributes) {
      return PlainTechClientAttributes.builder()
         .displayName(requestAttributes.getDisplayName())
         .description(requestAttributes.getDescription())
         .label(requestAttributes.getLabel())
         .build();
   }

   @ApiResponse(
      responseCode = "200",
      description = "The response contains all technical clients.",
      content = {
            @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = PlainTechClientDataCollectionDocument.class)),
            @Content(mediaType = "application/json", schema = @Schema(implementation = PlainTechClientDataCollectionDocument.class))
      }
   )
   @GET
   @Operation(
      description = "Retrieves the list of all technical clients.\n<p>\n<ul>\n<li><b>filter</b>: Restricts the collection of technical clients to be returned. See <a href=\"#filtering\">Filtering</a>.\nThe following filter names are available:\n<ul>\n<li><tt>displayName</tt>: Filters the technical client's name.</li>\n<li><tt>id</tt>: Filters the technical client's identifier.</li>\n<li><tt>label</tt>: Filters the technical client's label.</li>\n</ul>\n</li>\n<li><b>sort</b>: Sorts the collection of technical clients to be returned. See <a href=\"#sorting\">Sorting</a>.\nThe following sort identifier are available:\n<ul>\n<li><tt>displayName</tt>: Sorts on the technical client's name.</li>\n<li><tt>id</tt>: Sorts on the technical client's identifier.</li>\n<li><tt>label</tt>: Sorts on the technical client's label.</li>\n<li><tt>locked</tt>: Sorts on the technical client's locked flag.</li>\n<li><tt>creationDate</tt>: Sorts on the technical client's creation date.</li>\n</ul>\n</li>\n</ul>\n</p>\n<p>\nAll technical clients returned by this endpoint also contain the <tt>subscription</tt>\nand <tt>api-keys</tt> relationships if available on the specific technical client. An example can be found\non the endpoint that retrieves a single technical client.\n</p>\n<div class=\"iam-resource-return\">A collection document containing all technical clients.</div>",
      tags = "Technical Clients",
      summary = "List technical clients"
   )
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response retrieveAllClients(
      @Parameter(description = "Restricts the set of all technical clients to those on the requested page.") @BeanParam DocumentedFilterParameter filterParameter,
      @Parameter(description = "Restricts the set of all technical clients to those matching the filter condition.") @ValidPageParameter @BeanParam SanitizedPageParameter pageParameter,
      @Parameter(description = "Sorts the collected set of technical clients.") @BeanParam DocumentedSortParameter sortParameter
   ) {
      TechClientSearch techClientSearch = new TechClientSearch(filterParameter, pageParameter, sortParameter);
      Collection<PlainTechClient> allClients = this.plainTechClientService().findAllClients();
      TechClientSearch.TechClientSearchResult result = techClientSearch.search(allClients, pageParameter.getLimit());
      JsonApiCollectionMapper<PlainTechClient, PlainTechClientData> jsonApiMapper = this.mapper
         .forElements(result.getMatchingClients(), PlainTechClientData.class);
      TechClientResource.addCustomMappingContext(jsonApiMapper);
      CollectionDocument<PlainTechClientData> collection = jsonApiMapper.withIdSupplier(client -> client.getId().asString()).toCollection();
      UriBuilder uri = Resources.uri((ExternalUriInfo)this.uriInfo.get(), TechClientsResource.class)
         .replaceQuery(((ExternalUriInfo)this.uriInfo.get()).getRequestUri().getQuery());
      this.removeParametersWithName(uri, "page[limit]", "page[offset]");
      Pager pager = new Pager(uri);
      collection.setLinks(pager.createPagingLinks(pageParameter.getOffset(), pageParameter.getLimit(), result.getTotalCount()));
      return Response.ok(collection).build();
   }

   private void removeParametersWithName(UriBuilder uri, String... names) {
      Stream var10000 = Stream.of(names);
      UriBuilder var3 = uri;
      var10000.forEach(x$0 -> var3.replaceQueryParam(x$0, new Object[0]));
   }

   private String currentAdmin() {
      return ((CurrentAdministrator)this.currentAdministrator.get()).getName();
   }

   private PlainTechClientService plainTechClientService() {
      return (PlainTechClientService)this.plainTechClientService.get();
   }
}
