package com.airlock.iam.factor.domain.service.airlock2fa;

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.domain.model.event.UserEventFactory;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.common.domain.model.user.Lockable;
import com.airlock.iam.common.domain.service.token.UserSessionTokenService;
import com.airlock.iam.core.misc.authen.PredefinedLockReason;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.factor.domain.model.airlock2fa.AuthenticationStatus;
import com.airlock.iam.factor.domain.model.airlock2fa.AuthenticationStatusSettings;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.log.api.signing.SigningLogger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;

public class AuthenticationStatusService {
   private static final ELogger LOG = ELoggerFactory.getLogger(AuthenticationStatusService.class);
   private final AuthenticationStatusSettings settings;
   private final Provider<UserPrincipal> principal;
   private final SigningLogger signingLogger;
   private final HelpdeskLogger helpdeskLogger;
   private final Provider<UserEventFactory> eventFactory;
   private final Provider<UserSessionTokenService> userSessionTokenService;

   @Inject
   public AuthenticationStatusService(
      AuthenticationStatusSettings settings,
      Provider<UserPrincipal> principal,
      SigningLogger signingLogger,
      HelpdeskLogger helpdeskLogger,
      Provider<UserEventFactory> eventFactory,
      Provider<UserSessionTokenService> userSessionTokenService
   ) {
      this.settings = settings;
      this.principal = principal;
      this.signingLogger = signingLogger;
      this.helpdeskLogger = helpdeskLogger;
      this.eventFactory = eventFactory;
      this.userSessionTokenService = userSessionTokenService;
   }

   public void handleStatus(AuthenticationStatus status) {
      if (status == AuthenticationStatus.FRAUD) {
         this.lockUserOnFraud();
      }
   }

   private void lockUserOnFraud() {
      if (this.settings.lockUserOnFraud()) {
         UserId userId = ((UserPrincipal)this.principal.get()).getId();
         Lockable lockable = ((UserPrincipal)this.principal.get()).asLockable().orElseThrow();
         if (lockable.isLocked()) {
            LOG.info(String.format("Tried to lock user \"%s\" because of Airlock 2FA fraud report, but user was already locked.", userId.getValue()));
            return;
         }

         lockable.lock(PredefinedLockReason.FRAUD_REPORTED_BY_USER);
         ((UserSessionTokenService)this.userSessionTokenService.get()).handleUserLocked(userId);
         String fraudResourceKey = PredefinedLockReason.FRAUD_REPORTED_BY_USER.getResourceKey();
         this.signingLogger
            .log(
               "User \"%s\" rejected the Airlock 2FA authentication/approval attempt and reported fraud - user is locked with reason \"%s\".",
               new Object[]{userId.getValue(), fraudResourceKey}
            );
         this.helpdeskLogger
            .log(
               userId,
               "Rejected the Airlock 2FA authentication/approval attempt and reported fraud - user is locked with reason \"%s\".",
               new Object[]{fraudResourceKey}
            );
         ((UserEventFactory)this.eventFactory.get()).publishUserLockedEvent(userId, PredefinedLockReason.FRAUD_REPORTED_BY_USER);
      }
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class AuthenticationStatusServiceBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private AuthenticationStatusSettings settings;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<UserPrincipal> principal;
      @SuppressFBWarnings(justification = "generated code")
      private SigningLogger signingLogger;
      @SuppressFBWarnings(justification = "generated code")
      private HelpdeskLogger helpdeskLogger;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<UserEventFactory> eventFactory;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<UserSessionTokenService> userSessionTokenService;

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

      @SuppressFBWarnings(justification = "generated code")
      public AuthenticationStatusService.AuthenticationStatusServiceBuilder settings(AuthenticationStatusSettings settings) {
         this.settings = settings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AuthenticationStatusService.AuthenticationStatusServiceBuilder principal(Provider<UserPrincipal> principal) {
         this.principal = principal;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public AuthenticationStatusService.AuthenticationStatusServiceBuilder helpdeskLogger(HelpdeskLogger helpdeskLogger) {
         this.helpdeskLogger = helpdeskLogger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AuthenticationStatusService.AuthenticationStatusServiceBuilder eventFactory(Provider<UserEventFactory> eventFactory) {
         this.eventFactory = eventFactory;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AuthenticationStatusService.AuthenticationStatusServiceBuilder userSessionTokenService(Provider<UserSessionTokenService> userSessionTokenService) {
         this.userSessionTokenService = userSessionTokenService;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AuthenticationStatusService build() {
         return new AuthenticationStatusService(
            this.settings, this.principal, this.signingLogger, this.helpdeskLogger, this.eventFactory, this.userSessionTokenService
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "AuthenticationStatusService.AuthenticationStatusServiceBuilder(settings="
            + this.settings
            + ", principal="
            + this.principal
            + ", signingLogger="
            + this.signingLogger
            + ", helpdeskLogger="
            + this.helpdeskLogger
            + ", eventFactory="
            + this.eventFactory
            + ", userSessionTokenService="
            + this.userSessionTokenService
            + ")";
      }
   }
}
