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

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.common.api.infrastructure.annotation.RequiresWritableSession;
import com.airlock.iam.saml2.application.service.idp.Saml2SpInitiatedSsoService;
import com.airlock.iam.saml2.application.service.idp.Saml2SsoContinuationService;
import com.airlock.iam.saml2.domain.model.idp.Saml2IdpMetaAlias;
import com.airlock.iam.saml2.domain.model.idp.Saml2IdpSsoContinuationId;
import com.airlock.iam.saml2.domain.model.idp.Saml2RequestId;
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/sso")
public final class Saml2IdpSsoResource {
   private static final String META_ALIAS_IN_PARAMETER_PATTERN = "/[a-zA-Z0-9][a-zA-Z0-9-_.]{1,99}";
   public static final String ID_PARAMETER = "id";
   public static final String REQ_ID_PARAMETER = "ReqID";
   private final Provider<Saml2SsoContinuationService> ssoContinuationService;
   private final Provider<Saml2SpInitiatedSsoService> spInitiatedSsoService;

   @Inject
   Saml2IdpSsoResource(Provider<Saml2SsoContinuationService> ssoContinuationService, Provider<Saml2SpInitiatedSsoService> spInitiatedSsoService) {
      this.ssoContinuationService = ssoContinuationService;
      this.spInitiatedSsoService = spInitiatedSsoService;
   }

   @Path("metaAlias/{metaAlias}")
   @GET
   @Produces("text/html")
   @RequiresWritableSession
   public Response spInitiatedSsoByGet(
      @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.doSpInitiatedSso();
   }

   @Path("metaAlias/{metaAlias}")
   @POST
   @Produces("text/html")
   public Response spInitiatedSsoByPost(
      @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.doSpInitiatedSso();
   }

   @Path("continue")
   @GET
   @Produces("text/html")
   @RequiresWritableSession
   public Response ssoContinueEndpoint(
      @NotNull(message = "{id.notNull}") @Size(min = 1, max = 50, message = "{id.size}") @QueryParam("id") String continuationId,
      @Size(min = 1, max = 100, message = "{metaAlias.size}") @Pattern(regexp = "/[a-zA-Z0-9][a-zA-Z0-9-_.]{1,99}", message = "{metaAlias.pattern}") @QueryParam(
   "metaAlias"
) String metaAlias,
      @Size(min = 1, max = 1000, message = "{reqId.size}") @QueryParam("ReqID") String requestId
   ) {
      this.validateSsoContinueParams(requestId, metaAlias);
      if (requestId != null) {
         ((Saml2SsoContinuationService)this.ssoContinuationService.get())
            .spInitiatedSsoFederate(
               Saml2IdpSsoContinuationId.parseSsoContinuationId(continuationId), Saml2RequestId.requestId(requestId), Saml2IdpMetaAlias.idpMetaAlias(metaAlias)
            );
      } else {
         ((Saml2SsoContinuationService)this.ssoContinuationService.get())
            .idpInitiatedSsoFederate(Saml2IdpSsoContinuationId.parseSsoContinuationId(continuationId));
      }

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

   private void validateSsoContinueParams(String requestId, String metaAlias) {
      if (requestId != null && metaAlias == null || requestId == null && metaAlias != null) {
         throw new ViolationException("Either both requestId and metaAlias parameters or none must be present in the request.");
      }
   }

   private Response doSpInitiatedSso() {
      Optional<URI> redirectUri = ((Saml2SpInitiatedSsoService)this.spInitiatedSsoService.get()).handleSpInitiatedSso();
      return redirectUri.isPresent()
         ? Response.status(Status.FOUND).header("Location", redirectUri.get()).build()
         : Response.status(Saml2RequestThreadContext.getResponse().getStatus()).build();
   }
}
