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

import com.airlock.iam.authentication.api.domain.model.step.AuthenticationStepResult;
import com.airlock.iam.authentication.api.domain.model.step.AuthenticationStepResultFactory;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.common.api.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.common.api.domain.model.otp.UnverifiedOtp;
import com.airlock.iam.core.domain.model.authentication.factor.KnownAuthenticationFactorDetail;
import com.airlock.iam.flow.api.domain.model.step.GenericStepError;
import com.airlock.iam.flow.shared.application.configuration.vasco.CommonVascoOtpCheckingStepConfig;
import com.airlock.iam.flow.shared.domain.service.otp.OtpVerificationService;
import jakarta.inject.Inject;
import jakarta.inject.Provider;

@LogicForConfig(CommonVascoOtpCheckingStepConfig.class)
public class VascoOtpCheckingStepImpl implements VascoOtpCheckingStep {
   private final Provider<OtpVerificationService> service;
   private final Provider<AuthenticationMethod> authMethod;

   @Inject
   public VascoOtpCheckingStepImpl(Provider<OtpVerificationService> service, Provider<AuthenticationMethod> authMethod) {
      this.service = service;
      this.authMethod = authMethod;
   }

   @Override
   public AuthenticationStepResult initialize() {
      return !((OtpVerificationService)this.service.get()).hasToken()
         ? this.stepResultFactory().fail(GenericStepError.NO_VALID_TOKEN).isFailedFactor(false).build()
         : this.stepResultFactory().stay(VascoOtpCheckingStep.VascoOtpStepActions.VASCO_OTP_REQUIRED).build();
   }

   public AuthenticationStepResult checkOtp(UnverifiedOtp otp) {
      OtpVerificationService.OtpVerificationResult result = ((OtpVerificationService)this.service.get()).verifyOtp(otp);

      return switch (result) {
         case OTP_ACCEPTED -> this.stepResultFactory().success().build();
         case OTP_REJECTED -> this.stepResultFactory().fail(VascoOtpCheckingStep.VascoOtpStepErrors.VASCO_OTP_WRONG).build();
         case TOKEN_INACTIVE -> this.stepResultFactory().fail(GenericStepError.NO_VALID_TOKEN).isFailedFactor(false).build();
         case NEXT_OTP_REQUIRED -> throw new FailureException("Unexpected token check result: " + result);
      };
   }

   @Override
   public AuthenticationMethod getAuthenticationMethod() {
      return (AuthenticationMethod)this.authMethod.get();
   }

   @Override
   public String getDescription() {
      return "Vasco OTP Checking Step";
   }

   public AuthenticationStepResultFactory stepResultFactory() {
      return AuthenticationStepResultFactory.forFactorDetail(KnownAuthenticationFactorDetail.OTP_DIGIPASS);
   }
}
