package com.airlock.iam.flow.shared.infrastructure.service.recovery;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.domain.model.session.FlowSessionStatus;
import com.airlock.iam.flow.domain.model.session.InternalFlowSession;
import com.airlock.iam.flow.shared.domain.model.session.FlowBasedIamSession;
import com.airlock.iam.flow.shared.domain.service.session.SessionTicketPersist;
import com.airlock.iam.flow.shared.infrastructure.session.FlowBasedIamSessionSerializer;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.security.util.crypto.IamCipher;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Optional;

public final class SessionTicketPersistImpl implements SessionTicketPersist {
   private static final ELogger LOG = ELoggerFactory.getLogger(SessionTicketPersistImpl.class);
   private final HttpServletResponse response;
   private final HttpServletRequest request;
   private final Provider<IamCipher> cipher;
   private final Provider<FlowBasedIamSessionSerializer> mapifier;
   private final Provider<ConfigurationContext> context;

   @Inject
   public SessionTicketPersistImpl(
      HttpServletResponse response,
      HttpServletRequest request,
      Provider<IamCipher> cipher,
      Provider<FlowBasedIamSessionSerializer> mapifier,
      Provider<ConfigurationContext> context
   ) {
      this.response = response;
      this.request = request;
      this.cipher = cipher;
      this.mapifier = mapifier;
      this.context = context;
   }

   @Override
   public void persist(FlowBasedIamSession iamSession) {
      iamSession.setConfigurationContext((ConfigurationContext)this.context.get());
      this.serializeSession(iamSession).<String>map(((IamCipher)this.cipher.get())::encrypt).map(this::createCookie).ifPresent(this.response::addCookie);
   }

   private Optional<String> serializeSession(FlowBasedIamSession session) {
      if (session.userId().isEmpty()) {
         LOG.debug("Not persisting the user session because no user ID is present");
         return Optional.empty();
      }

      Optional<InternalFlowSession> flowSession = session.getUserSession().getCurrentFlowSession();
      if (flowSession.isPresent() && flowSession.get().getStatus() == FlowSessionStatus.STARTED) {
         throw new FailureException("Should not persist user session while a flow is active");
      }

      LOG.debug("Persisting user session in session ticket");
      return Optional.of(((FlowBasedIamSessionSerializer)this.mapifier.get()).serializeSession(session));
   }

   private Cookie createCookie(String encodedTicket) {
      Cookie cookie = new Cookie("IAM_FLOW_SESSION_TICKET", encodedTicket);
      cookie.setSecure(false);
      cookie.setPath(this.request.getContextPath());
      return cookie;
   }
}
