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

import com.airlock.iam.common.api.infrastructure.annotation.RequiresWritableSession;
import com.airlock.iam.saml2.application.service.idp.Saml2IdpInitiatedStartSloService;
import com.airlock.iam.saml2.application.service.idp.Saml2SloService;
import com.airlock.iam.saml2.application.service.idp.Saml2SpInitiatedResumeSloService;
import com.airlock.iam.saml2.domain.model.idp.logout.Saml2IdpInitiatedSloId;
import com.airlock.iam.saml2.domain.model.idp.logout.Saml2SpInitiatedSloId;
import com.airlock.iam.saml2.infrastructure.util.Saml2RequestThreadContext;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import java.net.URI;
import java.util.Optional;

@Path("/idp/slo")
public final class Saml2IdpSloResource {
   private final Provider<Saml2IdpInitiatedStartSloService> sloInitService;
   private final Provider<Saml2SloService> sloRedirectService;
   private final Provider<Saml2SpInitiatedResumeSloService> spInitiatedResumeSloService;

   @Inject
   Saml2IdpSloResource(
      Provider<Saml2IdpInitiatedStartSloService> sloInitService,
      Provider<Saml2SloService> sloRedirectService,
      Provider<Saml2SpInitiatedResumeSloService> spInitiatedResumeSloService
   ) {
      this.sloInitService = sloInitService;
      this.sloRedirectService = sloRedirectService;
      this.spInitiatedResumeSloService = spInitiatedResumeSloService;
   }

   @Path("init")
   @GET
   @Produces("text/html")
   @RequiresWritableSession
   public Response sloInit(
      @NotNull(message = "{id.notNull}") @Size(min = 36, max = 36, message = "{id.size}") @QueryParam("id") String sloIdParameter,
      @NotNull(message = "{location.notNull}") @Size(min = 1, max = 2048, message = "{location.size}") @QueryParam("Location") String uiLogoutResumeUriParameter
   ) {
      Saml2IdpInitiatedSloId sloId = Saml2IdpInitiatedSloId.parseIdpInitiatedSloId(sloIdParameter);
      ((Saml2IdpInitiatedStartSloService)this.sloInitService.get()).validateAndStoreUiLogoutResumeUri(uiLogoutResumeUriParameter);
      Optional<URI> redirectUri = ((Saml2IdpInitiatedStartSloService)this.sloInitService.get()).startIdpInitiatedSlo(sloId);
      return this.mapToResponse(redirectUri);
   }

   @Path("metaAlias/{metaAlias}")
   @GET
   @Produces("text/html")
   @RequiresWritableSession
   public Response sloRedirectByGet(
      @NotNull(message = "{metaAlias.notNull}") @Size(min = 1, max = 100, message = "{metaAlias.size}") @Pattern(
   regexp = "[a-zA-Z0-9][a-zA-Z0-9-_.]{1,99}",
   message = "{metaAlias.pattern}"
) @PathParam("metaAlias") String metaAlias
   ) {
      return this.doSloRedirect();
   }

   @Path("metaAlias/{metaAlias}")
   @POST
   @Produces("text/html")
   public Response sloRedirectByPost(
      @NotNull(message = "{metaAlias.notNull}") @Size(min = 1, max = 100, message = "{metaAlias.size}") @Pattern(
   regexp = "[a-zA-Z0-9][a-zA-Z0-9-_.]{1,99}",
   message = "{metaAlias.pattern}"
) @PathParam("metaAlias") String metaAlias
   ) {
      return this.doSloRedirect();
   }

   @Path("resume")
   @GET
   @Produces("text/html")
   @RequiresWritableSession
   public Response resumeSpInitiatedSlo(@NotNull(message = "{id.notNull}") @Size(min = 1, max = 50, message = "{id.size}") @QueryParam("id") String sloId) {
      ((Saml2SpInitiatedResumeSloService)this.spInitiatedResumeSloService.get()).resume(Saml2SpInitiatedSloId.parseSpInitiatedSloId(sloId));
      return this.responseFromLibrary();
   }

   private Response doSloRedirect() {
      Optional<URI> continueUri = ((Saml2SloService)this.sloRedirectService.get()).processSlo();
      return this.mapToResponse(continueUri);
   }

   private Response mapToResponse(Optional<URI> redirectUri) {
      return redirectUri.isPresent() ? Response.status(Status.FOUND).header("Location", redirectUri.get()).build() : this.responseFromLibrary();
   }

   private Response responseFromLibrary() {
      return Response.status(Saml2RequestThreadContext.getResponse().getStatus()).build();
   }
}
