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

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.common.api.domain.model.otp.UnverifiedOtp;
import com.airlock.iam.common.api.domain.model.user.CurrentLocale;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.core.misc.authen.AuthenticationSession;
import com.airlock.iam.core.misc.authen.AuthenticatorException;
import com.airlock.iam.core.misc.authen.OtpCheckAuthenticator;
import com.airlock.iam.core.misc.authen.credential.Credential;
import com.airlock.iam.core.misc.authen.credential.TokenCredential;
import com.airlock.iam.core.misc.authen.credential.UserCredential;
import com.airlock.iam.core.misc.authen.result.AuthenticationResult;
import com.airlock.iam.core.misc.impl.authen.credential.UserCredentialImpl;
import com.airlock.iam.core.misc.impl.authen.credential.UserTokenCredentialImpl;
import com.airlock.iam.flow.api.domain.model.step.StepSession;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

public class OtpCheckingAdapter {
   private final UserPrincipal principal;
   private final CurrentLocale locale;
   private final OtpCheckAuthenticator authenticator;
   private final Provider<OtpCheckStepSession> stepSession;
   private final OtpCheckingResultLogger logger;

   @Inject
   public OtpCheckingAdapter(
      UserPrincipal principal,
      CurrentLocale locale,
      OtpCheckAuthenticator authenticator,
      Provider<OtpCheckStepSession> stepSession,
      OtpCheckingResultLogger logger
   ) {
      this.principal = principal;
      this.locale = locale;
      this.authenticator = authenticator;
      this.stepSession = stepSession;
      this.logger = logger;
   }

   public Optional<StepSession> createSession() {
      try {
         return Optional.of(new OtpCheckStepSession(this.authenticator.getAuthenticationSession()));
      } catch (AuthenticatorException e) {
         throw new FailureException("unable to provide session from authenticator", e);
      }
   }

   public AuthenticationStepResult initialize(AuthenticationStepResultFactory stepResultFactory) {
      UserCredential credential = new UserCredentialImpl(this.principal.getName());
      return this.callAuthenticator(credential, ((OtpCheckStepSession)this.stepSession.get()).getAuthenticationSession(), stepResultFactory);
   }

   public AuthenticationStepResult checkOtp(UnverifiedOtp otp, AuthenticationStepResultFactory stepResultFactory) {
      TokenCredential credential = new UserTokenCredentialImpl(this.principal.getName(), otp.rawValue());
      return this.callAuthenticator(credential, ((OtpCheckStepSession)this.stepSession.get()).getAuthenticationSession(), stepResultFactory);
   }

   private AuthenticationStepResult callAuthenticator(
      Credential credential, AuthenticationSession authenticationSession, AuthenticationStepResultFactory stepResultFactory
   ) {
      try {
         credential.setDisplayLanguage(this.locale.getLanguage());
         AuthenticationResult authenticationResult = this.authenticator.getNextResult(authenticationSession, credential, this.principal.userOptional());
         this.logger.log(authenticationResult);
         return GenericOtpCheckingResultMapper.mapAuthenticationResult(authenticationResult, stepResultFactory);
      } catch (AuthenticatorException e) {
         throw new FailureException("Exception while calling authenticator", e);
      }
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public Provider<OtpCheckStepSession> getStepSession() {
      return this.stepSession;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OtpCheckingAdapterBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserPrincipal principal;
      @SuppressFBWarnings(justification = "generated code")
      private CurrentLocale locale;
      @SuppressFBWarnings(justification = "generated code")
      private OtpCheckAuthenticator authenticator;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<OtpCheckStepSession> stepSession;
      @SuppressFBWarnings(justification = "generated code")
      private OtpCheckingResultLogger logger;

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

      @SuppressFBWarnings(justification = "generated code")
      public OtpCheckingAdapter.OtpCheckingAdapterBuilder principal(UserPrincipal principal) {
         this.principal = principal;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OtpCheckingAdapter.OtpCheckingAdapterBuilder locale(CurrentLocale locale) {
         this.locale = locale;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OtpCheckingAdapter.OtpCheckingAdapterBuilder authenticator(OtpCheckAuthenticator authenticator) {
         this.authenticator = authenticator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OtpCheckingAdapter.OtpCheckingAdapterBuilder stepSession(Provider<OtpCheckStepSession> stepSession) {
         this.stepSession = stepSession;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OtpCheckingAdapter.OtpCheckingAdapterBuilder logger(OtpCheckingResultLogger logger) {
         this.logger = logger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OtpCheckingAdapter build() {
         return new OtpCheckingAdapter(this.principal, this.locale, this.authenticator, this.stepSession, this.logger);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OtpCheckingAdapter.OtpCheckingAdapterBuilder(principal="
            + this.principal
            + ", locale="
            + this.locale
            + ", authenticator="
            + this.authenticator
            + ", stepSession="
            + this.stepSession
            + ", logger="
            + this.logger
            + ")";
      }
   }
}
