package com.airlock.iam.flow.shared.domain.model.airlock2fa;

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FAAccountId;
import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceId;
import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceIdsProvider;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
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.factor.domain.model.airlock2fa.Airlock2FAAuthLogData;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAUser;
import com.airlock.iam.factor.domain.model.airlock2fa.event.Airlock2FADeviceEventFactory;
import com.airlock.iam.factor.domain.service.airlock2fa.Airlock2FAAuthApiUserService;
import com.airlock.iam.factor.domain.service.airlock2fa.repository.Airlock2FAUserRepository;
import com.airlock.iam.flow.api.domain.model.step.FlowStep;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.shared.application.configuration.airlock2fa.Airlock2FADeleteDevicesStepConfig;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.log.api.signing.SigningLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;

@LogicForConfig(Airlock2FADeleteDevicesStepConfig.class)
public final class Airlock2FADeleteDevicesStepImpl implements FlowStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(Airlock2FADeleteDevicesStepImpl.class);
   private final Provider<Airlock2FADeviceIdsProvider> devicesToDelete;
   private final Provider<Airlock2FAUserRepository> userRepository;
   private final Provider<PrincipalInfo> principalInfo;
   private final Provider<Airlock2FAAuthApiUserService> unenrollService;
   private final Provider<Airlock2FADeviceEventFactory> eventFactory;
   private final Provider<HelpdeskLogger> helpdeskLogger;
   private final Provider<SigningLogger> signingLogger;

   @Inject
   Airlock2FADeleteDevicesStepImpl(
      Provider<Airlock2FADeviceIdsProvider> devicesToDelete,
      Provider<Airlock2FAUserRepository> userRepository,
      Provider<PrincipalInfo> principalInfo,
      Provider<Airlock2FAAuthApiUserService> unenrollService,
      Provider<Airlock2FADeviceEventFactory> eventFactory,
      Provider<HelpdeskLogger> helpdeskLogger,
      Provider<SigningLogger> signingLogger
   ) {
      this.devicesToDelete = devicesToDelete;
      this.userRepository = userRepository;
      this.principalInfo = principalInfo;
      this.unenrollService = unenrollService;
      this.eventFactory = eventFactory;
      this.helpdeskLogger = helpdeskLogger;
      this.signingLogger = signingLogger;
   }

   @Override
   public StepResult initialize() {
      Optional<Airlock2FAAccountId> accountId = ((Airlock2FAUserRepository)this.userRepository.get())
         .findBy(this.getUserId())
         .map(Airlock2FAUser::getAccountId);
      if (accountId.isEmpty()) {
         LOG.debug("No Airlock 2FA account found. Not deleting any devices.");
         return this.stepResultFactory().success().build();
      }

      Set<Airlock2FADeviceId> deviceIdsForDeletion = new LinkedHashSet<>(((Airlock2FADeviceIdsProvider)this.devicesToDelete.get()).deviceIds());
      if (deviceIdsForDeletion.isEmpty()) {
         LOG.debug("Did not delete any Airlock 2FA devices since the configured Airlock 2FA device IDs provider did not provide any IDs.");
      } else {
         this.deleteDevices(accountId.get(), deviceIdsForDeletion);
      }

      return this.stepResultFactory().success().build();
   }

   private UserId getUserId() {
      return ((PrincipalInfo)this.principalInfo.get()).getKnownIdentity().orElseThrow().id();
   }

   private void deleteDevices(Airlock2FAAccountId accountId, Collection<Airlock2FADeviceId> deviceIdsToDelete) {
      UserId userId = this.getUserId();
      deviceIdsToDelete.forEach(deviceId -> {
         ((Airlock2FAAuthApiUserService)this.unenrollService.get()).unenrollDevice(accountId, deviceId);
         this.logDeviceDeletion(userId, Airlock2FAAuthLogData.airlock2FAAuthLogData(accountId).withDeviceId(deviceId));
         ((Airlock2FADeviceEventFactory)this.eventFactory.get()).publishAirlock2FADeviceDeletedEvent(userId, accountId, deviceId);
      });
   }

   private void logDeviceDeletion(UserId userId, Airlock2FAAuthLogData deviceLogData) {
      String quotedUserId = LogUtil.logSafeQuote(userId);
      String description = ((Airlock2FADeviceIdsProvider)this.devicesToDelete.get()).description();
      ((HelpdeskLogger)this.helpdeskLogger.get())
         .log(userId, "Deleted Airlock 2FA device because it was returned by %s %s", new Object[]{LogUtil.quote(description), deviceLogData});
      ((SigningLogger)this.signingLogger.get())
         .log("Deleted Airlock 2FA device of user %s because it was returned by %s %s".formatted(quotedUserId, LogUtil.quote(description), deviceLogData));
   }

   @Override
   public String getDescription() {
      return "Airlock 2FA Delete Devices Step";
   }
}
