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

import ch.ergon.restal.jsonapi.Metadata;
import ch.ergon.restal.jsonapi.document.data.ResourceObject;
import ch.ergon.restal.jsonapi.document.error.ErrorData;
import ch.ergon.restal.jsonapi.document.error.ErrorDocument;
import com.airlock.iam.admin.application.service.activation.ActivationStateWatcher;
import com.airlock.iam.admin.application.service.license.LicenseService;
import com.airlock.iam.admin.infrastructure.error.user.LicenseErrorCodeMapper;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.LicenseDataResourceDocument;
import com.airlock.iam.admin.infrastructure.generated.transfer.response.LicenseValidationWarningDataResourceDocument;
import com.airlock.iam.admin.infrastructure.transfer.request.LicenseUploadRequest;
import com.airlock.iam.admin.infrastructure.transfer.response.LicenseData;
import com.airlock.iam.admin.infrastructure.transfer.response.LicenseValidationWarningData;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.base.infrastructure.mapper.ValidationToErrorMapper;
import com.airlock.iam.common.application.jsonapi.ResourceIdentifierUtils;
import com.airlock.iam.common.infrastructure.exception.IamRestErrorCode;
import com.airlock.iam.license.domain.model.verification.input.License;
import com.airlock.iam.license.domain.model.verification.warnings.LicenseWarning;
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.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.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import ma.glasnost.orika.MapperFacade;

@Path("/license")
public class LicenseResource {
   private final Provider<LicenseService> licenseService;
   private final MapperFacade mapper;
   private final ValidationToErrorMapper errorMapper;

   @Inject
   public LicenseResource(Provider<LicenseService> licenseService, MapperFacade mapper, ValidationToErrorMapper errorMapper) {
      this.licenseService = licenseService;
      this.mapper = mapper;
      this.errorMapper = errorMapper;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Retrieved the active license.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = LicenseDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = LicenseDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "404",
               description = "There is no active license.",
               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
   @Operation(
      description = "Retrieves the active license.\n<div class=\"iam-resource-return\">The currently active license.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "License",
      summary = "Get active license"
   )
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response retrieveLicense() {
      Optional<License> license = ((LicenseService)this.licenseService.get()).getActiveLicense();
      if (license.isPresent()) {
         LicenseData data = this.buildLicenseData(license.get());
         return Response.ok(ResourceObject.builder(ResourceIdentifierUtils.getIdFor(data), data).document()).build();
      } else {
         return ErrorResponse.notFound().build().toResponse();
      }
   }

   private LicenseData buildLicenseData(License license) {
      Optional<String> licenseText = ((LicenseService)this.licenseService.get()).getActiveLicenseText();
      LicenseData licenseData = (LicenseData)this.mapper.map(license, LicenseData.class);
      licenseData.setLicenseKey(licenseText.orElse(""));
      ((LicenseService)this.licenseService.get()).getCurrentUsers().ifPresent(licenseData::setCurrentUsers);
      return licenseData;
   }

   private List<LicenseValidationWarningData.WarningData> getUploadLicenseWarningData(String license) {
      List<LicenseWarning> warnings = ((LicenseService)this.licenseService.get()).getLicenseWarnings(license);
      return this.buildLicenseWarningData(warnings);
   }

   private List<LicenseValidationWarningData.WarningData> buildLicenseWarningData(Collection<LicenseWarning> warnings) {
      return warnings.stream()
         .map(w -> (LicenseValidationWarningData.WarningData)this.mapper.map(w, LicenseValidationWarningData.WarningData.class))
         .collect(Collectors.toList());
   }

   @ApiResponses(
      {
            @ApiResponse(responseCode = "200", description = "License successfully activated."),
            @ApiResponse(
               responseCode = "400",
               description = "License could not be activated.",
               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
   @Operation(
      description = "Activates the license.\n<div class=\"iam-resource-return\">an empty response or an error document.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "License",
      summary = "Activate license"
   )
   @Path("/activate")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response activateLicense(@Parameter(description = "Contains the license to activate.") @Valid @NotNull LicenseUploadRequest request) {
      ActivationStateWatcher.ActivationResult result = ((LicenseService)this.licenseService.get()).activate(request.getLicense());
      if (result != ActivationStateWatcher.ActivationResult.SUCCESS && result != ActivationStateWatcher.ActivationResult.NO_ACTIVATION_STATES) {
         Metadata meta = new Metadata();
         meta.addValue("detail", result.name());
         ErrorData[] errors = new ErrorData[]{ErrorData.builder().code(IamRestErrorCode.CONFIG_ACTIVATION_FAILED.name()).meta(meta).build()};
         return Response.status(Status.BAD_REQUEST).entity(new ErrorDocument(errors)).build();
      } else {
         ((LicenseService)this.licenseService.get()).logAnalyticsSetting();
         return Response.ok().build();
      }
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Validation was successful and retrieved optional warnings.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = LicenseValidationWarningDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = LicenseValidationWarningDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "400",
               description = "Validation was not successful and retrieved the failures.",
               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
   @Operation(
      description = "Validates the license.\n<div class=\"iam-resource-return\">The list of validation failures. Might be empty.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "License",
      summary = "Validate license"
   )
   @Path("/validate")
   @Consumes("application/json")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response validateLicense(@Parameter(description = "Contains the license to validate.") @Valid @NotNull LicenseUploadRequest request) {
      ValidationResult result = ((LicenseService)this.licenseService.get()).validate(request.getLicense());
      if (!result.isValid()) {
         return this.errorMapper.withErrorCodeMapper(new LicenseErrorCodeMapper()).mapToResponse(result.getErrors());
      }

      List<LicenseValidationWarningData.WarningData> licenseWarningData = this.getUploadLicenseWarningData(request.getLicense());
      if (licenseWarningData.isEmpty()) {
         return Response.ok().build();
      }

      LicenseValidationWarningData data = new LicenseValidationWarningData(licenseWarningData);
      return Response.ok(ResourceObject.builder(ResourceIdentifierUtils.getIdFor(data), data).document()).build();
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "Retrieved warnings of the active license.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = LicenseValidationWarningDataResourceDocument.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = LicenseValidationWarningDataResourceDocument.class))
               }
            ),
            @ApiResponse(
               responseCode = "404",
               description = "There is no active license yet.",
               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
   @Operation(
      description = "Retrieves warnings of the active license.\n<div class=\"iam-resource-return\">Warnings regarding the active license.</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "License",
      summary = "List license warnings"
   )
   @Path("/warnings")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   public Response retrieveWarnings() {
      if (((LicenseService)this.licenseService.get()).getActiveLicense().isPresent()) {
         List<LicenseWarning> warnings = ((LicenseService)this.licenseService.get()).getActiveLicenseWarnings();
         List<LicenseValidationWarningData.WarningData> warningData = this.buildLicenseWarningData(warnings);
         LicenseValidationWarningData data = new LicenseValidationWarningData(warningData);
         return Response.ok(ResourceObject.builder(ResourceIdentifierUtils.getIdFor(data), data).document()).build();
      } else {
         return ErrorResponse.notFound().build().toResponse();
      }
   }
}
