package com.airlock.iam.authentication.domain.model.oath;

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.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.oath.CommonOathOtpCheckConfig;
import com.airlock.iam.flow.shared.domain.model.oath.OathOtpAuthenticationStep;
import com.airlock.iam.flow.shared.domain.service.otp.OtpVerificationService;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;

@LogicForConfig(CommonOathOtpCheckConfig.class)
public class OathOtpAuthenticationStepImpl implements OathOtpAuthenticationStep {
   private final Provider<OtpVerificationService> service;
   private final Provider<AuthenticationMethod> authMethod;

   @Inject
   public OathOtpAuthenticationStepImpl(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(OathOtpAuthenticationStep.OathOtpStepActions.OATH_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(OathOtpAuthenticationStep.OathOtpStepErrors.OATH_OTP_WRONG).build();
         case NEXT_OTP_REQUIRED -> this.stepResultFactory().stay(OathOtpAuthenticationStep.OathOtpStepActions.NEXT_OATH_OTP_REQUIRED).build();
         case TOKEN_INACTIVE -> this.stepResultFactory().fail(GenericStepError.NO_VALID_TOKEN).isFailedFactor(false).build();
      };
   }

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

   @Override
   public String getDescription() {
      return "OATH OTP Authentication Step";
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class OathOtpAuthenticationStepImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Provider<OtpVerificationService> service;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<AuthenticationMethod> authMethod;

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

      @SuppressFBWarnings(justification = "generated code")
      public OathOtpAuthenticationStepImpl.OathOtpAuthenticationStepImplBuilder service(Provider<OtpVerificationService> service) {
         this.service = service;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OathOtpAuthenticationStepImpl.OathOtpAuthenticationStepImplBuilder authMethod(Provider<AuthenticationMethod> authMethod) {
         this.authMethod = authMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OathOtpAuthenticationStepImpl build() {
         return new OathOtpAuthenticationStepImpl(this.service, this.authMethod);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OathOtpAuthenticationStepImpl.OathOtpAuthenticationStepImplBuilder(service=" + this.service + ", authMethod=" + this.authMethod + ")";
      }
   }
}
