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

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.api.domain.service.username.IdentityProvidingService;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.api.domain.model.validation.ValidatorError;
import com.airlock.iam.common.api.domain.model.user.ProvidedUsername;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.flow.api.domain.model.step.GenericStepError;
import com.airlock.iam.flow.api.domain.model.step.StepError;
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.publicselfservice.api.domain.model.PublicSelfServiceRestriction;
import com.airlock.iam.publicselfservice.application.configuration.steps.PublicSelfServiceUserIdentifyingStepConfig;
import com.airlock.iam.publicselfservice.domain.service.RestrictionViolationsService;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

@LogicForConfig(PublicSelfServiceUserIdentifyingStepConfig.class)
public class PublicSelfServiceUserIdentifyingStep implements UserIdentifyingStep, IdentityProvidingFlowStep {
   private final Provider<IdentityProvidingService> identificationService;
   private final Provider<RestrictionViolationsService> restrictionViolationsService;

   @Inject
   public PublicSelfServiceUserIdentifyingStep(
      Provider<IdentityProvidingService> identificationService, Provider<RestrictionViolationsService> restrictionViolationsService
   ) {
      this.restrictionViolationsService = restrictionViolationsService;
      this.identificationService = identificationService;
   }

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

   @Override
   public StepResult identifyUser(String providedUsername) {
      if (providedUsername.isBlank()) {
         return this.stepResultFactory()
            .failRetry(GenericStepError.VALIDATION_FAILED, UserIdentifyingStep.UserIdentifyingStepAction.USERNAME_REQUIRED)
            .resultData(new ValidationStepResultData(ValidationResult.singleError("username", GenericError.INVALID_VALUE)))
            .build();
      } else {
         IdentificationResult identifyResult = ((IdentityProvidingService)this.identificationService.get())
            .tryIdentify(ProvidedUsername.providedUsernameOf(providedUsername));
         if (identifyResult.hasError(IdentificationResult.IdentificationFailure.IdentificationError.USERNAME_INVALID)) {
            return this.stepResultFactory()
               .failRetry(GenericStepError.VALIDATION_FAILED, UserIdentifyingStep.UserIdentifyingStepAction.USERNAME_REQUIRED)
               .resultData(new ValidationStepResultData(ValidationResult.invalid(ValidatorError.validatorError("username", GenericError.WRONG_FORMAT))))
               .build();
         } else if (!identifyResult.hasError(IdentificationResult.IdentificationFailure.IdentificationError.CONCURRENT_ACCESS)
            && !identifyResult.hasError(IdentificationResult.IdentificationFailure.IdentificationError.DIFFERENT_USERNAME)) {
            UserIdentity identity = identifyResult.getUserIdentity().orElseThrow();
            Optional<PublicSelfServiceRestriction.ViolationCode> violationToReport = ((RestrictionViolationsService)this.restrictionViolationsService.get())
               .checkViolations(identity);
            return violationToReport.isPresent()
               ? this.stepResultFactory().fail(stepError(violationToReport.get())).build()
               : this.userIdentifiedSuccessResult(identity);
         } else {
            return IdentificationResultConverter.convertToStepResult(
                  identifyResult.getFailure().orElseThrow(), this.stepResultFactory(), UserIdentifyingStep.UserIdentifyingStepAction.USERNAME_REQUIRED
               )
               .build();
         }
      }
   }

   private static StepError stepError(PublicSelfServiceRestriction.ViolationCode violationCode) {
      return DynamicStepError.dynamicStepError(violationCode.name());
   }

   @Override
   public String getDescription() {
      return "User Identifying Step";
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class PublicSelfServiceUserIdentifyingStepBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Provider<IdentityProvidingService> identificationService;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<RestrictionViolationsService> restrictionViolationsService;

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

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUserIdentifyingStep.PublicSelfServiceUserIdentifyingStepBuilder identificationService(
         Provider<IdentityProvidingService> identificationService
      ) {
         this.identificationService = identificationService;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public PublicSelfServiceUserIdentifyingStep.PublicSelfServiceUserIdentifyingStepBuilder restrictionViolationsService(
         Provider<RestrictionViolationsService> restrictionViolationsService
      ) {
         this.restrictionViolationsService = restrictionViolationsService;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "PublicSelfServiceUserIdentifyingStep.PublicSelfServiceUserIdentifyingStepBuilder(identificationService="
            + this.identificationService
            + ", restrictionViolationsService="
            + this.restrictionViolationsService
            + ")";
      }
   }
}
