package com.airlock.iam.authentication.application.service.session;

import com.airlock.iam.authentication.domain.model.idpropagation.CurrentTargetApplication;
import com.airlock.iam.authentication.domain.model.session.SuccessfulAuthenticationMethods;
import com.airlock.iam.authentication.domain.service.ApplicationAccessCompletedService;
import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.application.service.session.AirlockControlCookieService;
import com.airlock.iam.common.domain.model.auth.idpropagation.WafCredential;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.common.domain.service.SessionService;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.session.FlowSession;
import com.airlock.iam.flow.domain.service.MultiFlowManager;
import com.airlock.iam.flow.shared.domain.model.session.ProcessedRemovedRoles;
import com.airlock.iam.flow.shared.domain.service.session.SessionTicketPersist;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.login.domain.model.session.AuditToken;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;

public final class ApplicationAccessCompletedServiceImpl implements ApplicationAccessCompletedService {
   private static final ELogger LOG = ELoggerFactory.getLogger(ApplicationAccessCompletedServiceImpl.class);
   private final Provider<MultiFlowManager> flowManager;
   private final Provider<CurrentTargetApplication> application;
   private final Provider<PrincipalInfo> principalInfo;
   private final Provider<AirlockControlCookieService> controlCookieService;
   private final Provider<Optional<AuditToken>> auditToken;
   private final Provider<HelpdeskLogger> helpdeskLogger;
   private final Provider<Optional<SessionTicketPersist>> userSessionPersistence;
   private final Provider<SessionService> sessionService;
   private final Provider<ProcessedRemovedRoles> processedRemovedRoles;

   @Inject
   public ApplicationAccessCompletedServiceImpl(
      Provider<MultiFlowManager> flowManager,
      Provider<CurrentTargetApplication> application,
      Provider<PrincipalInfo> principalInfo,
      Provider<AirlockControlCookieService> controlCookieService,
      Provider<Optional<AuditToken>> auditToken,
      Provider<HelpdeskLogger> helpdeskLogger,
      Provider<Optional<SessionTicketPersist>> userSessionPersistence,
      Provider<SessionService> sessionService,
      Provider<ProcessedRemovedRoles> processedRemovedRoles
   ) {
      this.flowManager = flowManager;
      this.application = application;
      this.principalInfo = principalInfo;
      this.controlCookieService = controlCookieService;
      this.auditToken = auditToken;
      this.helpdeskLogger = helpdeskLogger;
      this.userSessionPersistence = userSessionPersistence;
      this.sessionService = sessionService;
      this.processedRemovedRoles = processedRemovedRoles;
   }

   @Override
   public void handleApplicationAccessCompleted() {
      LOG.debug("Handling completed authorization flow for application " + LogUtil.quote(((CurrentTargetApplication)this.application.get()).getApplicationId()));
      this.logSuccessfulAuthenticationAndAuthorization(
         this.userId(),
         ((MultiFlowManager)this.flowManager.get())
            .getCurrentFlowSession()
            .map(FlowSession::flowAttributes)
            .flatMap(attrs -> attrs.tryGetAttribute(SuccessfulAuthenticationMethods.class))
            .map(SuccessfulAuthenticationMethods::getDescription)
            .orElse("-"),
         ((CurrentTargetApplication)this.application.get()).getApplicationId().getApplicationId()
      );
      this.propagateIdentity();
      this.setAuditTokenAndWafCredentials();
      ((MultiFlowManager)this.flowManager.get()).destroyCurrentMultiFlow();
      ((Optional)this.userSessionPersistence.get()).ifPresent(wafPresent -> ((SessionService)this.sessionService.get()).terminateSessionAtEndOfRestRequest());
   }

   private void logSuccessfulAuthenticationAndAuthorization(UserId username, String authMethodDesc, String applId) {
      if (StringUtils.isNotBlank(authMethodDesc)) {
         ((HelpdeskLogger)this.helpdeskLogger.get())
            .log(
               username,
               "User '%s' is successfully authenticated by '%s' and is authorized to access application '%s'.",
               new Object[]{username, authMethodDesc, applId}
            );
      } else {
         ((HelpdeskLogger)this.helpdeskLogger.get())
            .log(username, "User '%s' was already sufficiently authenticated and is authorized to access application '%s'.", new Object[]{username, applId});
      }
   }

   private void propagateIdentity() {
      ((CurrentTargetApplication)this.application.get())
         .propagateIdentity(ViolationException.checkPresent(((PrincipalInfo)this.principalInfo.get()).getAuthenticatedIdentity(), "expecting a userId"));
   }

   private void setAuditTokenAndWafCredentials() {
      AirlockControlCookieService service = (AirlockControlCookieService)this.controlCookieService.get();
      ((Optional)this.auditToken.get()).ifPresent(token -> service.setAuditToken(token.getValue()));
      List<WafCredential> wafCredentials = ((CurrentTargetApplication)this.application.get()).getWafCredentials();
      service.addCredentialsWithKeepSessionId(wafCredentials);
      ProcessedRemovedRoles processed = (ProcessedRemovedRoles)this.processedRemovedRoles.get();
      wafCredentials.forEach(cred -> processed.forgetRemovedRole(cred.getCredentialName()));
   }

   private UserId userId() {
      return ((PrincipalInfo)this.principalInfo.get()).tryGetAuthenticatedIdentity().id();
   }
}
