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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.elog.api.LogTypes;
import com.airlock.iam.flow.api.application.configuration.step.FlowStepConfig;
import com.airlock.iam.flow.api.domain.model.flowcontrol.InteractiveGotoTarget;
import com.airlock.iam.flow.api.domain.model.step.StepAction;
import com.airlock.iam.flow.api.domain.model.step.StepId;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.api.domain.model.step.StepResultFactory;
import com.airlock.iam.flow.domain.model.InteractiveGotoTargetCollection;
import com.airlock.iam.flow.domain.model.session.CurrentlyExpectedStepAction;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;

public final class GotoStepAdapter implements GotoCapableStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(GotoStepAdapter.class);
   private final StepResultFactory stepResultFactory;
   private final FlowStepConfig stepConfig;
   private final Provider<CurrentlyExpectedStepAction> currentlyExpectedStepAction;
   private final String originalStepDescription;

   @Override
   public StepResult retrieveGotoTargets() {
      return this.stepResultFactory
         .stay(this.determineNextStepAction())
         .resultData(new InteractiveGotoTargetCollection(this.determineInteractiveGotoTargets()))
         .build();
   }

   @Override
   public StepResult doGoto(StepId targetStepId) {
      Optional<InteractiveGotoTarget> target = this.determineInteractiveGotoTargets()
         .stream()
         .filter(t -> t.getTargetStepId().getStepId().equals(targetStepId.getStepId()))
         .findFirst();
      if (target.isEmpty()) {
         LOG.warn(LogTypes.SERVER_INFO_SYS, String.format("Failed to execute goto because goto step with ID '%s' is not available.", targetStepId.getStepId()));
         return this.stepResultFactory.failRetry(GotoCapableStep.GotoCapableStepError.GOTO_FAILED, this.determineNextStepAction()).build();
      } else {
         return this.goTo(target.get());
      }
   }

   @Override
   public StepResult initialize() {
      throw new UnsupportedOperationException("should never be called");
   }

   @Override
   public String getDescription() {
      return "Goto Adapter for " + this.originalStepDescription;
   }

   @Override
   public StepResultFactory stepResultFactory() {
      return this.stepResultFactory;
   }

   private StepAction determineNextStepAction() {
      return ((CurrentlyExpectedStepAction)this.currentlyExpectedStepAction.get())
         .stepAction()
         .orElseThrow(() -> new FailureException("Expect flow session to contain last step result with next step action, but it did not."));
   }

   private StepResult goTo(InteractiveGotoTarget target) {
      return target.isTreatedAsFailure()
         ? this.stepResultFactory.failGoto(GotoCapableStep.GotoCapableStepError.FAIL_GOTO_TRIGGERED, target.getTargetStepId()).build()
         : this.stepResultFactory.doGoto(target.getTargetStepId()).build();
   }

   private List<InteractiveGotoTarget> determineInteractiveGotoTargets() {
      return this.stepConfig.getInteractiveGotoTargets();
   }

   @SuppressFBWarnings(justification = "generated code")
   public GotoStepAdapter(
      StepResultFactory stepResultFactory,
      FlowStepConfig stepConfig,
      Provider<CurrentlyExpectedStepAction> currentlyExpectedStepAction,
      String originalStepDescription
   ) {
      this.stepResultFactory = stepResultFactory;
      this.stepConfig = stepConfig;
      this.currentlyExpectedStepAction = currentlyExpectedStepAction;
      this.originalStepDescription = originalStepDescription;
   }
}
