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

import com.airlock.iam.common.infrastructure.factory.uri.AbsoluteExternalViewUriFactory;
import com.airlock.iam.core.domain.model.http.HttpRequestMethod;
import com.airlock.iam.saml2.application.service.idp.Saml2SpInitiatedStartSloService;
import com.airlock.iam.saml2.domain.model.idp.Saml2RequestParameter;
import com.airlock.iam.saml2.domain.model.idp.logout.Saml2SpInitiatedSloAttributes;
import com.airlock.iam.saml2.domain.model.idp.logout.Saml2SpInitiatedSloId;
import com.airlock.iam.saml2.domain.model.session.FlowFederationSession;
import com.airlock.iam.saml2.domain.model.session.FlowFederationSessionRepository;
import com.airlock.iam.saml2.infrastructure.idp.logout.Saml2SpInitiatedSloCache;
import com.airlock.iam.saml2.infrastructure.request.Saml2Request;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.Optional;
import org.apache.commons.lang3.Validate;

public final class Saml2SpInitiatedStartSloServiceImpl implements Saml2SpInitiatedStartSloService {
   private final Provider<Saml2Request> request;
   private final Provider<FlowFederationSessionRepository> federationSessionRepository;
   private final Provider<Saml2SpInitiatedSloCache> spInitiatedSloCache;
   private final Provider<URI> spInitiatedLogoutUri;
   private final Provider<AbsoluteExternalViewUriFactory> externalViewUriFactory;

   @Inject
   Saml2SpInitiatedStartSloServiceImpl(
      Provider<Saml2Request> request,
      Provider<FlowFederationSessionRepository> federationSessionRepository,
      Provider<Saml2SpInitiatedSloCache> spInitiatedSloCache,
      Provider<URI> spInitiatedLogoutUri,
      Provider<AbsoluteExternalViewUriFactory> externalViewUriFactory
   ) {
      this.request = request;
      this.federationSessionRepository = federationSessionRepository;
      this.spInitiatedSloCache = spInitiatedSloCache;
      this.spInitiatedLogoutUri = spInitiatedLogoutUri;
      this.externalViewUriFactory = externalViewUriFactory;
   }

   @Override
   public URI startSpInitiatedSlo() {
      FlowFederationSession session = this.initializeFederationSessionForSpInitiatedSlo();
      Saml2SpInitiatedSloAttributes sloAttributes = this.buildSloAttributes(this.extractLogoutRequest());
      Saml2SpInitiatedSloId sloId = ((Saml2SpInitiatedSloCache)this.spInitiatedSloCache.get()).save(session, sloAttributes);
      return this.redirectUri(sloId);
   }

   private FlowFederationSession initializeFederationSessionForSpInitiatedSlo() {
      return ((FlowFederationSessionRepository)this.federationSessionRepository.get())
         .getFederationSession()
         .orElseGet(() -> ((FlowFederationSessionRepository)this.federationSessionRepository.get()).createNewFederationSession())
         .setSpInitiatedSingleLogoutInProgressOnIdp();
   }

   private Saml2RequestParameter extractLogoutRequest() {
      Validate.isTrue(((Saml2Request)this.request.get()).samlResponse().isEmpty(), "Unexpected SAMLResponse parameter in SP-initiated SLO", new Object[0]);
      Validate.isTrue(((Saml2Request)this.request.get()).samlRequest().isPresent(), "Parameter SAMLRequest is mandatory for SP-initiated SLO", new Object[0]);
      return ((Saml2Request)this.request.get()).samlRequest().get();
   }

   private Saml2SpInitiatedSloAttributes buildSloAttributes(Saml2RequestParameter logoutRequest) {
      Saml2Request saml2Request = (Saml2Request)this.request.get();
      Saml2SpInitiatedSloAttributes.Saml2SpInitiatedSloAttributesBuilder attributesBuilder = Saml2SpInitiatedSloAttributes.builder()
         .httpMethod(HttpRequestMethod.valueOf(saml2Request.getMethod()))
         .requestUri(saml2Request.getRequestURI())
         .queryString(Optional.ofNullable(saml2Request.getQueryString()))
         .samlSloRequest(logoutRequest)
         .relayState(saml2Request.relayStateParameter())
         .signatureAlgorithm(Optional.empty())
         .signature(Optional.empty());
      if (this.isRedirectBinding(saml2Request)) {
         attributesBuilder.signatureAlgorithm(saml2Request.signatureAlgorithmParameter()).signature(saml2Request.signatureParameter());
      }

      return attributesBuilder.build();
   }

   private boolean isRedirectBinding(Saml2Request saml2Request) {
      return "GET".equals(saml2Request.getMethod());
   }

   private URI redirectUri(Saml2SpInitiatedSloId id) {
      return UriBuilder.fromUri(((AbsoluteExternalViewUriFactory)this.externalViewUriFactory.get()).createAbsoluteUri((URI)this.spInitiatedLogoutUri.get()))
         .queryParam("Location", new Object[]{this.locationParameter(id)})
         .queryParam("skipDisclaimer", new Object[]{true})
         .build(new Object[0]);
   }

   private String locationParameter(Saml2SpInitiatedSloId id) {
      return UriBuilder.fromUri(((AbsoluteExternalViewUriFactory)this.externalViewUriFactory.get()).createAbsoluteUri(Saml2IdpJerseyUrls.SLO_RESUME_URI.uri()))
         .queryParam("id", new Object[]{id.value()})
         .build(new Object[0])
         .toASCIIString();
   }
}
