package com.airlock.iam.flow.shared.infrastructure.session;

import com.airlock.iam.common.domain.model.contextdata.ContextRetentionPolicySettings;
import com.airlock.iam.common.domain.model.session.ReadOnlyIamSessionData;
import com.airlock.iam.common.domain.service.SessionService;
import com.airlock.iam.common.domain.service.session.IamSessionService;
import com.airlock.iam.common.infrastructure.exceptions.DeserializationFailedException;
import com.airlock.iam.common.infrastructure.filter.general.IamSessionProcessing;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.shared.api.domain.model.session.UserSessionStatus;
import com.airlock.iam.flow.shared.domain.model.session.FlowBasedIamSession;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

public final class FlowBasedIamSessionProcessing implements IamSessionProcessing {
   private static final ELogger LOG = ELoggerFactory.getLogger(FlowBasedIamSessionProcessing.class);
   private final Provider<IamSessionService<FlowBasedIamSession>> iamSessionService;
   private final Provider<SessionService> sessionService;
   private final Provider<FlowBasedIamSession> iamSession;
   private final Provider<ConfigurationContext> configurationContext;
   private final Provider<Optional<ReadOnlyIamSessionData>> readOnlyIamSessionData;
   private final Provider<ContextRetentionPolicySettings> contextRetentionPolicy;

   @Inject
   public FlowBasedIamSessionProcessing(
      Provider<IamSessionService<FlowBasedIamSession>> iamSessionService,
      Provider<SessionService> sessionService,
      Provider<FlowBasedIamSession> iamSession,
      Provider<ConfigurationContext> configurationContext,
      Provider<Optional<ReadOnlyIamSessionData>> readOnlyIamSessionData,
      Provider<ContextRetentionPolicySettings> contextRetentionPolicy
   ) {
      this.iamSessionService = iamSessionService;
      this.sessionService = sessionService;
      this.iamSession = iamSession;
      this.configurationContext = configurationContext;
      this.readOnlyIamSessionData = readOnlyIamSessionData;
      this.contextRetentionPolicy = contextRetentionPolicy;
   }

   @Override
   public void updateIamSessionAndReleaseLock() {
      if (((FlowBasedIamSession)this.iamSession.get()).isLockAcquired() && !((SessionService)this.sessionService.get()).isSessionMarkedForTermination()) {
         if (((FlowBasedIamSession)this.iamSession.get()).getUserSession().getStatus() == UserSessionStatus.TERMINATED) {
            ((SessionService)this.sessionService.get()).terminateSessionAtEndOfRestRequest();
         } else {
            ((IamSessionService)this.iamSessionService.get()).updateSession((FlowBasedIamSession)this.iamSession.get());
         }
      }
   }

   @Override
   public void updateSessionContextIfNecessary() {
      if (this.contextRetentionPolicy.get() == ContextRetentionPolicySettings.SESSION) {
         ConfigurationContext determinedContext = (ConfigurationContext)this.configurationContext.get();
         Optional<ConfigurationContext> contextFromSession = ((Optional)this.readOnlyIamSessionData.get()).flatMap(ReadOnlyIamSessionData::initialContext);
         if (contextFromSession.isEmpty() || !contextFromSession.get().equals(determinedContext)) {
            try {
               FlowBasedIamSession writableIamSession = (FlowBasedIamSession)((IamSessionService)this.iamSessionService.get()).retrieveOrCreateSession();
               logContextUpdate(contextFromSession, determinedContext);
               ((IamSessionService)this.iamSessionService.get()).updateSession(writableIamSession);
            } catch (DeserializationFailedException e) {
               LOG.debug("Not updating session context because session deserialization failed: " + e.getMessage());
            }
         }
      }
   }

   private static void logContextUpdate(Optional<ConfigurationContext> contextFromSession, ConfigurationContext determinedContext) {
      if (contextFromSession.isPresent()) {
         LOG.debug("Updating context in session from %s to %s".formatted(contextFromSession.get(), determinedContext));
      } else {
         LOG.debug("Setting initial context in session to %s".formatted(determinedContext));
      }
   }
}
