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

import ch.ergon.restal.jsonapi.document.data.CollectionDocument;
import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import ch.ergon.restal.jsonapi.document.data.ResourceObject;
import com.airlock.iam.admin.application.service.token.vasco.VascoLicenseService;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.user.tokens.VascoCrontoLicenseDataResourceDocument;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.user.tokens.VascoCrontoLicenseImportResultCollectionDocument;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.user.tokens.VascoOtpLicenseDataResourceDocument;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.user.tokens.VascoOtpLicenseImportResultCollectionDocument;
import com.airlock.iam.admin.infrastructure.transfer.response.user.tokens.VascoCrontoLicenseData;
import com.airlock.iam.admin.infrastructure.transfer.response.user.tokens.VascoOtpLicenseData;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
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.misc.io.TempFile;
import com.airlock.iam.common.application.jsonapi.ResourceIdentifierUtils;
import com.airlock.iam.common.infrastructure.resource.Resources;
import com.airlock.iam.core.misc.impl.token.VascoToken.AuthenticationMode;
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.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 java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.glassfish.jersey.media.multipart.FormDataParam;

@Path("tokens")
public class VascoLicenseResource {
   private static final Lock LOCK = new ReentrantLock();
   private final Provider<VascoLicenseService> service;
   private final RestMapper mapper;
   private final Provider<ExternalUriInfo> uriInfo;

   @Inject
   public VascoLicenseResource(Provider<VascoLicenseService> service, RestMapper mapper, Provider<ExternalUriInfo> uriInfo) {
      this.service = service;
      this.mapper = mapper;
      this.uriInfo = uriInfo;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "201",
               description = "The import was successful.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = VascoOtpLicenseImportResultCollectionDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = VascoOtpLicenseImportResultCollectionDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "400",
               description = "Wrong type or other failure.",
               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("multipart/form-data")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Path("/vasco-otp/license/import")
   @Operation(
      description = "Import a Vasco license file for OTP devices.\nOnly licenses of type \"response only\" (RO) are imported, all\nother types cause a failure.\n<div class=\"iam-resource-return\">List of serial IDs of all imported devices.</div>",
      tags = "Vasco OTP",
      summary = "Import OTP license file"
   )
   public Response importOtpLicense(
      @Parameter(description = "The license file.") @FormDataParam("file") InputStream file,
      @Parameter(description = "The license key.") @FormDataParam("licenseKey") String licenseKey
   ) {
      return this.importLicense(file, licenseKey, Optional.empty(), AuthenticationMode.RESPONSE_ONLY, "token.vasco-otp.license.import");
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "201",
               description = "The import was successful.",
               content = {
                     @Content(
                        mediaType = "application/vnd.api+json",
                        schema = @Schema(implementation = VascoCrontoLicenseImportResultCollectionDocument.class)
                     ),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = VascoCrontoLicenseImportResultCollectionDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "400",
               description = "Wrong type or other failure.",
               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("multipart/form-data")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Path("/vasco-cronto/license/import")
   @Operation(
      description = "Import a Vasco license file for Cronto accounts.\nOnly licenses of type \"master activation\" (MA) are imported, all\nother types  cause a failure.\n<div class=\"iam-resource-return\">list of serial IDs of all imported Cronto accounts.</div>",
      tags = "Vasco OTP",
      summary = "Import Cronto license file"
   )
   public Response importCrontoLicense(
      @Parameter(description = "The license file.") @FormDataParam("file") InputStream file,
      @Parameter(description = "The license key.") @FormDataParam("licenseKey") String licenseKey,
      @Parameter(description = "The software vector (required when enabled in VascoHandler, optional otherwise).") @FormDataParam("softwareVector") String softwareVector
   ) {
      return this.importLicense(
         file, licenseKey, Optional.ofNullable(softwareVector), AuthenticationMode.MASTER_ACTIVATION, "token.vasco-cronto.license.import"
      );
   }

   private Response importLicense(InputStream inputStream, String licenseKey, Optional<String> softwareVector, AuthenticationMode mode, String resultType) {
      if (LOCK.tryLock()) {
         try {
            List<String> importedIds = inputStream != null
               ? this.importFromFile(inputStream, licenseKey, softwareVector, mode)
               : ((VascoLicenseService)this.service.get()).importLicenseWithoutFile(licenseKey, softwareVector, mode);
            CollectionDocument<Object> document = new CollectionDocument();
            importedIds.stream().map(id -> ResourceObject.builder(resultType, id).build()).forEach(document::add);
            URI resourceUri = Resources.uri((ExternalUriInfo)this.uriInfo.get(), this.getClass()).build(new Object[0]);
            return Response.created(resourceUri).entity(document).build();
         } finally {
            LOCK.unlock();
         }
      } else {
         return ErrorResponse.badRequest().log("Another Vasco license import is already running").build().toResponse();
      }
   }

   private List<String> importFromFile(InputStream inputStream, String licenseKey, Optional<String> softwareVector, AuthenticationMode mode) {
      try {
         TempFile dpxFile = new TempFile("license-", ".dpx");

         List var6;
         try {
            Files.copy(inputStream, dpxFile.get().toPath(), StandardCopyOption.REPLACE_EXISTING);
            var6 = ((VascoLicenseService)this.service.get()).importLicenseFile(dpxFile.get(), licenseKey, softwareVector, mode);
         } catch (Throwable var9) {
            try {
               dpxFile.close();
            } catch (Throwable var8) {
               var9.addSuppressed(var8);
            }

            throw var9;
         }

         dpxFile.close();
         return var6;
      } catch (IOException e) {
         throw new FailureException(e);
      }
   }

   @ApiResponse(
      responseCode = "200",
      description = "The license information could be retrieved.",
      content = {
            @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = VascoOtpLicenseDataResourceDocument.class)),
            @Content(mediaType = "application/json", schema = @Schema(implementation = VascoOtpLicenseDataResourceDocument.class))
      }
   )
   @Operation(
      description = "Returns information about Vasco OTP licenses.\n<div class=\"iam-resource-return\">Statistical information about this license.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "Vasco OTP",
      summary = "Get Vasco OTP license info"
   )
   @GET
   @Path("/vasco-otp/license")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response getOtpLicenseInfo() {
      ResourceDocument<VascoOtpLicenseData> document = this.mapper
         .forElement(((VascoLicenseService)this.service.get()).getLicenseInfo(AuthenticationMode.RESPONSE_ONLY), VascoOtpLicenseData.class)
         .withIdSupplier(ResourceIdentifierUtils::getIdFor)
         .toResourceDocument();
      return Response.ok(document).build();
   }

   @ApiResponse(
      responseCode = "200",
      description = "The license information could be retrieved.",
      content = {
            @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = VascoCrontoLicenseDataResourceDocument.class)),
            @Content(mediaType = "application/json", schema = @Schema(implementation = VascoCrontoLicenseDataResourceDocument.class))
      }
   )
   @Operation(
      description = "Returns information about Vasco Cronto licenses.\n<div class=\"iam-resource-return\">Statistical information about this license.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "Vasco OTP",
      summary = "Get Cronto license info"
   )
   @GET
   @Path("/vasco-cronto/license")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response getCrontoLicenseInfo() {
      ResourceDocument<VascoCrontoLicenseData> document = this.mapper
         .forElement(((VascoLicenseService)this.service.get()).getLicenseInfo(AuthenticationMode.MASTER_ACTIVATION), VascoCrontoLicenseData.class)
         .withIdSupplier(ResourceIdentifierUtils::getIdFor)
         .toResourceDocument();
      return Response.ok(document).build();
   }
}
