package com.airlock.iam.login.domain.model.fingerprinting;

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.domain.model.logoutaction.LogoutAction;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.common.domain.model.session.WafSessionTerminationReason;
import com.airlock.iam.common.domain.model.user.Lockable;
import com.airlock.iam.common.domain.service.request.AirlockEnvCookiesInformationService;
import com.airlock.iam.common.domain.service.token.UserSessionTokenService;
import com.airlock.iam.core.misc.authen.PredefinedLockReason;
import com.airlock.iam.log.api.signing.SigningLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

public final class ClientFingerprintingLogoutAction implements LogoutAction {
   private final Provider<AirlockEnvCookiesInformationService> environmentCookieService;
   private final Provider<UserPrincipal> principal;
   private final Provider<Optional<ClientFingerprintingLockoutThreshold>> thresholdProvider;
   private final Provider<SigningLogger> signingLogger;
   private final Provider<UserSessionTokenService> userSessionTokenService;

   @Inject
   public ClientFingerprintingLogoutAction(
      Provider<AirlockEnvCookiesInformationService> environmentCookieService,
      Provider<UserPrincipal> principal,
      Provider<Optional<ClientFingerprintingLockoutThreshold>> thresholdProvider,
      Provider<SigningLogger> signingLogger,
      Provider<UserSessionTokenService> userSessionTokenService
   ) {
      this.environmentCookieService = environmentCookieService;
      this.principal = principal;
      this.thresholdProvider = thresholdProvider;
      this.signingLogger = signingLogger;
      this.userSessionTokenService = userSessionTokenService;
   }

   @Override
   public boolean isApplicable() {
      if (!((UserPrincipal)this.principal.get()).exists()) {
         return false;
      }

      Optional<ClientFingerprintingLockoutThreshold> threshold = (Optional<ClientFingerprintingLockoutThreshold>)this.thresholdProvider.get();
      return threshold.isPresent()
         ? ((AirlockEnvCookiesInformationService)this.environmentCookieService.get())
            .getWafSessionTerminationReason()
            .filter(wafSessionTerminationReason -> wafSessionTerminationReason instanceof WafSessionTerminationReason.ClientFingerprinting)
            .map(wafSessionTerminationReason -> (WafSessionTerminationReason.ClientFingerprinting)wafSessionTerminationReason)
            .flatMap(WafSessionTerminationReason.ClientFingerprinting::totalPenaltyPoints)
            .map(points -> points >= threshold.get().threshold())
            .orElse(false)
         : false;
   }

   @Override
   public void execute() {
      UserId userId = ((UserPrincipal)this.principal.get()).getId();
      Lockable lockable = ((UserPrincipal)this.principal.get()).asLockable().orElseThrow();
      if (lockable.isLocked()) {
         if (!lockable.isSelfUnlockSupported()) {
            ((SigningLogger)this.signingLogger.get())
               .log(String.format("Tried to lock user \"%s\" because of Client Fingerprinting, but user was already locked.", userId.getValue()));
            return;
         }

         ((SigningLogger)this.signingLogger.get())
            .log(
               String.format(
                  "User \"%s\" was already locked with a reason that allowed self unlock. Client Fingerprinting is thus set as the new lock reason and self unlock is disabled.",
                  userId.getValue()
               )
            );
      } else {
         ((SigningLogger)this.signingLogger.get())
            .log(String.format("Client Fingerprinting Lockout Threshold reached: Locking user \"%s\"", userId.getValue()));
      }

      lockable.lock(PredefinedLockReason.WAF_SESSION_TERMINATION);
      ((UserSessionTokenService)this.userSessionTokenService.get()).handleUserLocked(userId);
   }
}
