package com.airlock.iam.flow.shared.application.service;

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.application.service.session.SessionTerminationService;
import com.airlock.iam.common.application.service.user.LogoutService;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.application.service.flow.FlowTerminationService;
import com.airlock.iam.flow.domain.model.statemachine.InternalFlow;
import com.airlock.iam.flow.domain.service.MultiFlowManager;
import com.airlock.iam.flow.shared.api.domain.model.session.UserSession;
import com.airlock.iam.flow.types.api.domain.model.session.MultiFlowType;
import com.airlock.iam.log.api.signing.SigningLogger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

public final class FlowTerminationServiceImpl implements FlowTerminationService, LogoutService {
   private static final ELogger LOG = ELoggerFactory.getLogger(FlowTerminationServiceImpl.class);
   private final Provider<UserSession> userSession;
   private final Provider<MultiFlowManager> flowManager;
   private final SigningLogger signingLogger;
   private final Provider<PrincipalInfo> principalInfo;
   private final Provider<SessionTerminationService> sessionTerminationService;
   private final Provider<InternalFlow> flow;
   private final Provider<MultiFlowType> multiFlowType;

   @Inject
   public FlowTerminationServiceImpl(
      Provider<UserSession> userSession,
      Provider<MultiFlowManager> flowManager,
      SigningLogger signingLogger,
      Provider<PrincipalInfo> principalInfo,
      Provider<SessionTerminationService> sessionTerminationService,
      Provider<InternalFlow> flow,
      Provider<MultiFlowType> multiFlowType
   ) {
      this.userSession = userSession;
      this.flowManager = flowManager;
      this.signingLogger = signingLogger;
      this.principalInfo = principalInfo;
      this.sessionTerminationService = sessionTerminationService;
      this.flow = flow;
      this.multiFlowType = multiFlowType;
   }

   @Override
   public boolean abortFlow() {
      if (!((MultiFlowManager)this.flowManager.get()).hasCurrentFlow()) {
         return false;
      }

      Optional<UserId> userId = this.optionalUserId();
      this.finalizeCurrentFlowStep();
      if (!((MultiFlowManager)this.flowManager.get()).destroyCurrentMultiFlow()) {
         return false;
      }

      if (userId.isPresent()) {
         this.signingLogger.log("Flow for user " + LogUtil.logSafeQuote(userId.get()) + " has been aborted.");
      } else {
         this.signingLogger.log("Flow has been aborted.");
      }

      return true;
   }

   @Override
   public void clearUserSessionAndLogOut() {
      Optional<UserId> userId = this.optionalUserId();
      this.finalizeCurrentFlowStep();
      ((SessionTerminationService)this.sessionTerminationService.get()).terminateSessions();
      ((UserSession)this.userSession.get()).clear();
      if (userId.isPresent()) {
         this.signingLogger.log("Logout has been called for user " + LogUtil.logSafeQuote(userId.get().getValue()) + ".");
      } else {
         this.signingLogger.log("Logout has been called (no user associated with the current session).");
      }
   }

   private void finalizeCurrentFlowStep() {
      Optional<MultiFlowType> currentFlowType = ((MultiFlowManager)this.flowManager.get()).getCurrentMultiFlowType();
      if (currentFlowType.isPresent() && currentFlowType.get() == this.multiFlowType.get()) {
         ((InternalFlow)this.flow.get()).finalizeCurrentStep();
      } else {
         LOG.debug("Cannot finalize current step because the current flow is not of type " + this.multiFlowType.get());
      }
   }

   private Optional<UserId> optionalUserId() {
      return ((PrincipalInfo)this.principalInfo.get()).getKnownIdentity().map(UserIdentity::id);
   }

   @SuppressFBWarnings(justification = "generated code")
   public static FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder builder() {
      return new FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class FlowTerminationServiceImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Provider<UserSession> userSession;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<MultiFlowManager> flowManager;
      @SuppressFBWarnings(justification = "generated code")
      private SigningLogger signingLogger;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<PrincipalInfo> principalInfo;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<SessionTerminationService> sessionTerminationService;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<InternalFlow> flow;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<MultiFlowType> multiFlowType;

      @SuppressFBWarnings(justification = "generated code")
      FlowTerminationServiceImplBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder userSession(Provider<UserSession> userSession) {
         this.userSession = userSession;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder flowManager(Provider<MultiFlowManager> flowManager) {
         this.flowManager = flowManager;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder signingLogger(SigningLogger signingLogger) {
         this.signingLogger = signingLogger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder principalInfo(Provider<PrincipalInfo> principalInfo) {
         this.principalInfo = principalInfo;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder sessionTerminationService(
         Provider<SessionTerminationService> sessionTerminationService
      ) {
         this.sessionTerminationService = sessionTerminationService;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder flow(Provider<InternalFlow> flow) {
         this.flow = flow;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder multiFlowType(Provider<MultiFlowType> multiFlowType) {
         this.multiFlowType = multiFlowType;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowTerminationServiceImpl build() {
         return new FlowTerminationServiceImpl(
            this.userSession, this.flowManager, this.signingLogger, this.principalInfo, this.sessionTerminationService, this.flow, this.multiFlowType
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "FlowTerminationServiceImpl.FlowTerminationServiceImplBuilder(userSession="
            + this.userSession
            + ", flowManager="
            + this.flowManager
            + ", signingLogger="
            + this.signingLogger
            + ", principalInfo="
            + this.principalInfo
            + ", sessionTerminationService="
            + this.sessionTerminationService
            + ", flow="
            + this.flow
            + ", multiFlowType="
            + this.multiFlowType
            + ")";
      }
   }
}
