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

import ch.ergon.restal.jsonapi.document.data.ResourceDocument;
import com.airlock.iam.authentication.application.configuration.targetapp.TargetApplicationsConfig;
import com.airlock.iam.authentication.application.settings.AccessSettings;
import com.airlock.iam.authentication.infrastructure.service.ui.AuthenticationUiService;
import com.airlock.iam.authentication.infrastructure.transfer.response.ui.AccessConfigData;
import com.airlock.iam.authentication.infrastructure.transfer.response.ui.AuthenticationNonFlowUiConfigData;
import com.airlock.iam.authentication.infrastructure.transfer.response.ui.OnFailureAuthenticationData;
import com.airlock.iam.authentication.infrastructure.transfer.response.ui.OnLogoutConfigData;
import com.airlock.iam.authentication.infrastructure.transfer.response.ui.authentication.ApplicationIdData;
import com.airlock.iam.base.infrastructure.error.ErrorResponse;
import com.airlock.iam.base.infrastructure.mapper.entity.RestMapper;
import com.airlock.iam.common.api.infrastructure.annotation.RequiresWritableSession;
import com.airlock.iam.common.application.jsonapi.ResourceIdentifierUtils;
import com.airlock.iam.flow.api.domain.model.flow.ApplicationId;
import com.airlock.iam.flow.ui.infrastructure.service.OnLogout;
import com.airlock.iam.flow.ui.infrastructure.settings.FlowFailureRedirectSettings;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.Explode;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.enums.ParameterStyle;
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.constraints.Size;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import java.util.Optional;

@Path("/ui")
public class AuthenticationUiConfigurationResource {
   private final Provider<AuthenticationUiService> service;
   private final Provider<Optional<TargetApplicationsConfig>> authFlowConfig;
   private final RestMapper mapper;

   @Inject
   public AuthenticationUiConfigurationResource(
      Provider<AuthenticationUiService> service, Provider<Optional<TargetApplicationsConfig>> authFlowConfig, RestMapper mapper
   ) {
      this.service = service;
      this.authFlowConfig = authFlowConfig;
      this.mapper = mapper;
   }

   @ApiResponses(
      {
            @ApiResponse(
               responseCode = "200",
               description = "The response contains the default application ID.",
               content = {
                     @Content(mediaType = "application/vnd.api+json", schema = @Schema(implementation = ApplicationIdData.class)),
                     @Content(mediaType = "application/json", schema = @Schema(implementation = ApplicationIdData.class))
               }
            ),
            @ApiResponse(
               responseCode = "404",
               description = "If no UI is available.",
               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"))
               }
            )
      }
   )
   @Operation(
      description = "Returns the default application ID for authentication.\n<div class=\"iam-resource-return\">A JSON containing the default application ID</div>",
      extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")),
      tags = "UI",
      summary = "Get app ID"
   )
   @GET
   @Path("/default/application/id")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @RequiresWritableSession
   public Response getDefaultTargetApplicationId() {
      if (((Optional)this.authFlowConfig.get()).isEmpty()) {
         return ErrorResponse.notFound().build().toResponse();
      }

      ApplicationId applicationId = ((TargetApplicationsConfig)((Optional)this.authFlowConfig.get()).get()).getDefaultApplication().getApplicationId();
      return Response.ok().entity(new ApplicationIdData(applicationId.getApplicationId())).build();
   }

   @Operation(extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")))
   @GET
   @Path("/non-flow/configuration")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @RequiresWritableSession
   public Response getNonFlowUIConfiguration() {
      ResourceDocument<AuthenticationNonFlowUiConfigData> document = this.mapper
         .forElement(this.service().getNonFlowAuthenticationUiSettings(), AuthenticationNonFlowUiConfigData.class)
         .withIdSupplier(ResourceIdentifierUtils::getIdFor)
         .toResourceDocument();
      return Response.ok(document).build();
   }

   @Operation(extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")))
   @GET
   @Path("/configuration/logout")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @Parameters(
      @Parameter(
         in = ParameterIn.QUERY,
         name = "params",
         style = ParameterStyle.FORM,
         explode = Explode.TRUE,
         schema = @Schema(type = "object", implementation = Object.class)
      )
   )
   @RequiresWritableSession
   public Response getLogoutConfiguration() {
      OnLogout onLogout = this.service().getOnLogout();
      return Response.ok(this.mapper.map(onLogout, OnLogoutConfigData.class)).build();
   }

   @Operation(extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")))
   @GET
   @Path("/configuration/access")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @RequiresWritableSession
   public Response getAccessConfiguration() {
      Optional<AccessSettings> settings = this.service().getAccessSettings();
      return settings.isEmpty() ? ErrorResponse.notFound().build().toResponse() : Response.ok(this.mapper.map(settings.get(), AccessConfigData.class)).build();
   }

   @Operation(extensions = @Extension(properties = @ExtensionProperty(name = "x-internal", value = "true")))
   @GET
   @Path("/on-failure/{flowId}")
   @Produces({"application/vnd.api+json", "application/json;qs=0.9"})
   @RequiresWritableSession
   public Response getOnFailureInfo(@PathParam("flowId") @Size(min = 1, max = 30) String id) {
      ApplicationId applicationId = ApplicationId.applicationId(id);
      FlowFailureRedirectSettings settings = this.service().getOnFailureSettings(applicationId);
      return this.toResponse(applicationId, settings);
   }

   private Response toResponse(ApplicationId applicationId, FlowFailureRedirectSettings settings) {
      ResourceDocument<OnFailureAuthenticationData> document = this.mapper
         .forElement(settings, OnFailureAuthenticationData.class)
         .withIdSupplier(s -> applicationId.getApplicationId())
         .toResourceDocument();
      return Response.ok(document).build();
   }

   private AuthenticationUiService service() {
      return (AuthenticationUiService)this.service.get();
   }
}
