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

import com.airlock.iam.authentication.api.domain.model.step.IdentityProvidingFlowStep;
import com.airlock.iam.authentication.api.domain.model.step.UserIdentifyingStep;
import com.airlock.iam.authentication.api.domain.service.username.IdentificationResult;
import com.airlock.iam.authentication.api.domain.service.username.IdentificationResultConverter;
import com.airlock.iam.authentication.application.configuration.identification.UserByDataIdentifyingStepConfig;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.misc.result.Result;
import com.airlock.iam.flow.api.domain.model.step.GenericStepError;
import com.airlock.iam.flow.api.domain.model.step.StepAction;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.domain.model.ValidationStepResultData;
import com.airlock.iam.flow.domain.model.step.DynamicStepError;
import com.airlock.iam.flow.shared.domain.model.userdata.IdentificationByUserDataFailure;
import com.airlock.iam.flow.shared.domain.service.identification.IdentityProvidingByUserDataService;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Collections;
import java.util.Map;

@LogicForConfig(UserByDataIdentifyingStepConfig.class)
public class UserByDataIdentifyingStepImpl implements UserIdentifyingStep, IdentityProvidingFlowStep {
   private final Provider<IdentityProvidingByUserDataService> identificationService;

   @Inject
   public UserByDataIdentifyingStepImpl(Provider<IdentityProvidingByUserDataService> identificationService) {
      this.identificationService = identificationService;
   }

   @Override
   public StepResult initialize() {
      return this.stepResultFactory().stay(UserIdentifyingStep.UserIdentifyingStepAction.USER_DATA_REQUIRED).build();
   }

   @Override
   public StepResult identifyUser(String providedUsername) {
      return this.identifyUserByUserData(Collections.emptyMap());
   }

   @Override
   public StepResult identifyUserByUserData(Map<String, String> userData) {
      Result<IdentificationResult, IdentificationByUserDataFailure> result = ((IdentityProvidingByUserDataService)this.identificationService.get())
         .tryIdentifyByUserData(userData);
      if (result.isFailure()) {
         return switch (result.getFailure().error()) {
            case USER_DATA_INVALID, USER_DATA_MISSING -> this.getValidationErrorStepResult(
               result.getFailure().validationResult().orElseThrow(() -> new FailureException("Expected validation result to be present"))
            );
            case USER_DATA_NOT_UNIQUE, USER_DATA_NOT_FOUND -> this.stepResultFactory()
               .fail(DynamicStepError.dynamicStepError(IdentificationResult.IdentificationFailure.IdentificationError.USER_NOT_FOUND.name()))
               .build();
         };
      } else {
         return result.get().isFailure()
            ? IdentificationResultConverter.convertToStepResult(
                  result.get().getFailure().orElseThrow(), this.stepResultFactory(), UserIdentifyingStep.UserIdentifyingStepAction.USER_DATA_REQUIRED
               )
               .build()
            : this.userIdentifiedSuccessResult(result.get().getUserIdentity().orElseThrow());
      }
   }

   @Override
   public StepAction stepAction() {
      return UserIdentifyingStep.UserIdentifyingStepAction.USER_DATA_REQUIRED;
   }

   private StepResult getValidationErrorStepResult(ValidationResult validationResult) {
      return this.stepResultFactory()
         .failRetry(GenericStepError.VALIDATION_FAILED, UserIdentifyingStep.UserIdentifyingStepAction.USER_DATA_REQUIRED)
         .resultData(new ValidationStepResultData(validationResult))
         .build();
   }

   @Override
   public String getDescription() {
      return "Identify User By User Data Step";
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class UserByDataIdentifyingStepImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Provider<IdentityProvidingByUserDataService> identificationService;

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

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepImpl.UserByDataIdentifyingStepImplBuilder identificationService(
         Provider<IdentityProvidingByUserDataService> identificationService
      ) {
         this.identificationService = identificationService;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserByDataIdentifyingStepImpl build() {
         return new UserByDataIdentifyingStepImpl(this.identificationService);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "UserByDataIdentifyingStepImpl.UserByDataIdentifyingStepImplBuilder(identificationService=" + this.identificationService + ")";
      }
   }
}
