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

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.user.CurrentLocale;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.core.domain.model.authentication.factor.KnownAuthenticationFactorDetail;
import com.airlock.iam.core.misc.authen.AuthenticatorException;
import com.airlock.iam.core.misc.authen.credential.Credential;
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.MatrixAuthenticator;
import com.airlock.iam.core.misc.impl.authen.credential.UserCredentialImpl;
import com.airlock.iam.flow.api.domain.model.step.StepSession;
import com.airlock.iam.flow.shared.application.configuration.matrix.CommonMatrixCheckingStepConfig;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;

@LogicForConfig(CommonMatrixCheckingStepConfig.class)
public class MatrixCheckingStepImpl implements MatrixCheckingStep {
   public static final String TAN_CHALLENGE_AUTHENTICATOR = "tan_challenge_authenticator";
   private final Provider<MatrixAuthenticator> authenticator;
   private final Provider<AuthenticationMethod> authMethod;
   private final Provider<UserPrincipal> principal;
   private final Provider<CurrentLocale> locale;
   private final Provider<MatrixCheckingStepSession> stepSession;

   @Inject
   public MatrixCheckingStepImpl(
      Provider<MatrixAuthenticator> authenticator,
      Provider<AuthenticationMethod> authMethod,
      Provider<UserPrincipal> principal,
      Provider<CurrentLocale> locale,
      Provider<MatrixCheckingStepSession> stepSession
   ) {
      this.authenticator = authenticator;
      this.authMethod = authMethod;
      this.principal = principal;
      this.locale = locale;
      this.stepSession = stepSession;
   }

   @Override
   public AuthenticationStepResult initialize() {
      UserCredential credential = new UserCredentialImpl(((UserPrincipal)this.principal.get()).getName());
      credential.setDisplayLanguage(((CurrentLocale)this.locale.get()).getLanguage());
      return this.callAuthenticator(credential);
   }

   @Override
   public Optional<StepSession> createSession() {
      return Optional.of(new MatrixCheckingStepSession(((MatrixAuthenticator)this.authenticator.get()).getAuthenticationSession()));
   }

   public AuthenticationStepResult getCurrentChallenge() {
      return !((MatrixCheckingStepSession)this.stepSession.get()).isExpectingResponseVerification()
         ? this.stepResultFactory().failDueToUnexpectedCall("no current matrix challenge").build()
         : this.stepResultFactory()
            .stay(MatrixCheckingStepActions.MATRIX_RESPONSE_REQUIRED)
            .resultData(((MatrixCheckingStepSession)this.stepSession.get()).getCurrentChallenge().orElseThrow())
            .build();
   }

   public AuthenticationStepResult checkResponse(List<MatrixChallengeResponse> responses) {
      if (!((MatrixCheckingStepSession)this.stepSession.get()).isExpectingResponseVerification()) {
         return this.stepResultFactory().failDueToUnexpectedCall("no matrix check expected").build();
      }

      List<String> expectedCoordinates = ((MatrixCheckingStepSession)this.stepSession.get()).getCurrentChallenge().orElseThrow().getChallenges();
      return this.callAuthenticator(MatrixCheckingResultMapper.mapResponsesToCredential(responses, expectedCoordinates, (CurrentLocale)this.locale.get()));
   }

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

   @Override
   public String getDescription() {
      return "Matrix Card or Index List Authentication Step";
   }

   private AuthenticationStepResult callAuthenticator(Credential credential) {
      try {
         AuthenticationResult authResult = ((MatrixAuthenticator)this.authenticator.get())
            .getNextResult(
               ((MatrixCheckingStepSession)this.stepSession.get()).getAuthenticationSession(), credential, ((UserPrincipal)this.principal.get()).userOptional()
            );
         return MatrixCheckingResultMapper.mapResultAndUpdateSession(authResult, (MatrixCheckingStepSession)this.stepSession.get(), this.stepResultFactory());
      } catch (AuthenticatorException e) {
         throw new FailureException("Exception while calling matrix authenticator", e);
      }
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class MatrixCheckingStepImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Provider<MatrixAuthenticator> authenticator;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<AuthenticationMethod> authMethod;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<UserPrincipal> principal;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<CurrentLocale> locale;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<MatrixCheckingStepSession> stepSession;

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

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCheckingStepImpl.MatrixCheckingStepImplBuilder authenticator(Provider<MatrixAuthenticator> authenticator) {
         this.authenticator = authenticator;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCheckingStepImpl.MatrixCheckingStepImplBuilder principal(Provider<UserPrincipal> principal) {
         this.principal = principal;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCheckingStepImpl.MatrixCheckingStepImplBuilder locale(Provider<CurrentLocale> locale) {
         this.locale = locale;
         return this;
      }

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

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

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