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

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FAAccountId;
import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceId;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAFactor;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAFactorSet;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAPayloadEncryptionSettings;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAUserInformation;
import com.airlock.iam.factor.domain.service.airlock2fa.Airlock2FAUserInformationService;
import com.airlock.iam.flow.types.api.domain.model.session.KnownMultiFlowType;
import com.airlock.iam.flow.types.api.domain.model.session.MultiFlowType;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.function.Supplier;

public class Airlock2FAAuthTokenIdService {
   private final Provider<MultiFlowType> multiFlowType;
   private final Provider<Airlock2FAUserInformationService> userInformationService;
   private final Provider<Airlock2FAFlowSessionAttributesService> attributesService;
   private final Provider<Airlock2FAPayloadEncryptionSettings> payloadEncryptionSettings;

   @Inject
   public Airlock2FAAuthTokenIdService(
      Provider<MultiFlowType> multiFlowType,
      Provider<Airlock2FAUserInformationService> userInformationService,
      Provider<Airlock2FAFlowSessionAttributesService> attributesService,
      Provider<Airlock2FAPayloadEncryptionSettings> payloadEncryptionSettings
   ) {
      this.multiFlowType = multiFlowType;
      this.userInformationService = userInformationService;
      this.attributesService = attributesService;
      this.payloadEncryptionSettings = payloadEncryptionSettings;
   }

   public void handleSuccessfulUsage(Airlock2FAFactor factor, Airlock2FAAccountId accountId, Airlock2FADeviceId deviceId) {
      this.saveAuthTokenId(factor, deviceId, () -> this.determineAllowedFactors(accountId, deviceId));
   }

   public void handleSuccessfulUsage(Airlock2FAFactor factor, Airlock2FAUserInformation userInfo, Airlock2FADeviceId deviceId) {
      this.saveAuthTokenId(factor, deviceId, () -> this.determineAllowedFactors(deviceId, userInfo));
   }

   private void saveAuthTokenId(Airlock2FAFactor factor, Airlock2FADeviceId deviceId, Supplier<Airlock2FAFactorSet> allowedFactors) {
      if (this.isEligibleFlow()) {
         ((Airlock2FAFlowSessionAttributesService)this.attributesService.get()).saveAuthTokenId(factor, deviceId, allowedFactors.get());
      }
   }

   private boolean isEligibleFlow() {
      return this.multiFlowType.get() == KnownMultiFlowType.AUTHENTICATION_AND_AUTHORIZATION || this.multiFlowType.get() == KnownMultiFlowType.USER_SELF_REG;
   }

   private Airlock2FAFactorSet determineAllowedFactors(Airlock2FAAccountId accountId, Airlock2FADeviceId deviceId) {
      return this.determineAllowedFactors(deviceId, ((Airlock2FAUserInformationService)this.userInformationService.get()).getUser(accountId).orElseThrow());
   }

   private Airlock2FAFactorSet determineAllowedFactors(Airlock2FADeviceId deviceId, Airlock2FAUserInformation userInformation) {
      Airlock2FAFactorSet allowedFactors = Airlock2FAFactorSet.of(userInformation.getAllowedFactors());
      Airlock2FAFactorSet deviceFactors = userInformation.getAuthDevices()
         .deviceWithId(deviceId)
         .map(device -> device.getSupportedFactors((Airlock2FAPayloadEncryptionSettings)this.payloadEncryptionSettings.get()))
         .orElse(Airlock2FAFactorSet.allAirlock2FAUserFactors());
      return allowedFactors.intersect(deviceFactors);
   }
}
