package com.airlock.iam.publicselfservice.domain.model.step;

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.core.domain.model.authentication.factor.KnownAuthenticationFactorDetail;
import com.airlock.iam.core.domain.model.cronto.CrontoCheckingSettings;
import com.airlock.iam.core.domain.model.cronto.CrontoOtp;
import com.airlock.iam.core.misc.impl.cronto.SimulatedCrontoChallenge;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.step.StepSession;
import com.airlock.iam.flow.shared.domain.model.cronto.CrontoAuthChallenge;
import com.airlock.iam.flow.shared.domain.model.cronto.CrontoCheckingStep;
import com.airlock.iam.publicselfservice.application.configuration.steps.CrontoPublicSelfServiceApprovalStealthStepConfig;
import com.airlock.iam.publicselfservice.domain.model.flow.session.CrontoApprovalStealthStepSession;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Collections;
import java.util.Optional;

@LogicForConfig(CrontoPublicSelfServiceApprovalStealthStepConfig.class)
public class CrontoApprovalStealthStep implements CrontoCheckingStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(CrontoApprovalStealthStep.class);
   private final CrontoCheckingSettings settings;
   private final Provider<AuthenticationMethod> authMethod;
   private final Provider<CrontoApprovalStealthStepSession> stepSession;

   @Inject
   public CrontoApprovalStealthStep(
      CrontoCheckingSettings settings, Provider<AuthenticationMethod> authMethod, Provider<CrontoApprovalStealthStepSession> stepSession
   ) {
      this.settings = settings;
      this.authMethod = authMethod;
      this.stepSession = stepSession;
   }

   @Override
   public Optional<StepSession> createSession() {
      return Optional.of(
         new CrontoApprovalStealthStepSession(this.settings.getMaxResponseRetries(), this.settings.getCrontoHandler().createSimulatedChallenge())
      );
   }

   @Override
   public AuthenticationStepResult initialize() {
      return this.stepResultFactory().stay(CrontoCheckingStep.CrontoAuthStepActions.CRONTO_OTP_REQUIRED).build();
   }

   public AuthenticationStepResult checkOtp(CrontoOtp otp) {
      ((CrontoApprovalStealthStepSession)this.stepSession.get()).increaseNumberOfTries();
      return ((CrontoApprovalStealthStepSession)this.stepSession.get()).isFailedDefinitely()
         ? this.stepResultFactory().fail(CrontoCheckingStep.CrontoAuthStepErrors.CRONTO_OTP_WRONG).build()
         : this.stepResultFactory()
            .failRetry(CrontoCheckingStep.CrontoAuthStepErrors.CRONTO_OTP_WRONG, CrontoCheckingStep.CrontoAuthStepActions.CRONTO_OTP_REQUIRED)
            .build();
   }

   public AuthenticationStepResult retrieveChallenge() {
      return this.stepResultFactory()
         .stay(CrontoCheckingStep.CrontoAuthStepActions.CRONTO_OTP_REQUIRED)
         .resultData(mapSimulatedChallengeToAuthChallenge(((CrontoApprovalStealthStepSession)this.stepSession.get()).getCurrentChallenge()))
         .build();
   }

   public AuthenticationStepResult pollOnlineValidation(boolean queryOnly) {
      return this.stepResultFactory()
         .failDueToUnexpectedCall("Online Validation impossible, since push is always disabled in the Cronto factor stealth step.")
         .build();
   }

   public AuthenticationStepResult getPushDeviceChoices() {
      return this.stepResultFactory()
         .failDueToUnexpectedCall("Push device choice impossible, as push is always disabled in the Cronto factor stealth step.")
         .build();
   }

   public AuthenticationStepResult selectPushDevice(String id) {
      return this.stepResultFactory()
         .failDueToUnexpectedCall("Push device selection impossible, as push is always disabled in the Cronto factor stealth step.")
         .build();
   }

   public AuthenticationStepResult selectOfflineAuthentication() {
      return this.stepResultFactory()
         .failDueToUnexpectedCall("Switch to offline impossible, as the Cronto factor stealth step should never allow online authentication.")
         .build();
   }

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

   @Override
   public String getDescription() {
      return "Cronto Factor Stealth Step";
   }

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

   private static CrontoAuthChallenge mapSimulatedChallengeToAuthChallenge(SimulatedCrontoChallenge challenge) {
      return CrontoAuthChallenge.builder()
         .base64Cryptogram(challenge.getCryptogramForScreen().base64image())
         .secureChannelChallenge(challenge.getRawChallengeString())
         .onlineValidation(false)
         .pushed(false)
         .pushLoginId(Optional.empty())
         .pushDevices(Collections.emptyList())
         .build();
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class CrontoApprovalStealthStepBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private CrontoCheckingSettings settings;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<AuthenticationMethod> authMethod;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<CrontoApprovalStealthStepSession> stepSession;

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

      @SuppressFBWarnings(justification = "generated code")
      public CrontoApprovalStealthStep.CrontoApprovalStealthStepBuilder settings(CrontoCheckingSettings settings) {
         this.settings = settings;
         return this;
      }

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "CrontoApprovalStealthStep.CrontoApprovalStealthStepBuilder(settings="
            + this.settings
            + ", authMethod="
            + this.authMethod
            + ", stepSession="
            + this.stepSession
            + ")";
      }
   }
}
