package com.airlock.iam.flow.infrastructure.factories;

import com.airlock.iam.base.api.infrastructure.plugin.PluginConfigToLogic;
import com.airlock.iam.base.infrastructure.provider.CachingDelegationProvider;
import com.airlock.iam.flow.api.application.configuration.selection.SelectionFlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.FlowStepConfig;
import com.airlock.iam.flow.api.domain.model.selection.Option;
import com.airlock.iam.flow.api.domain.model.step.FlowStep;
import com.airlock.iam.flow.domain.model.statemachine.runtime.FlowStateFactory;
import com.airlock.iam.flow.domain.model.statemachine.spring.StepContext;
import com.airlock.iam.flow.domain.model.step.FlowStepConditionChecker;
import com.airlock.iam.flow.domain.model.step.FlowStepConditionCheckerFactory;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

public final class StepContextFactory {
   private final PluginConfigToLogic configToLogic;
   private final FlowStepConditionCheckerFactory conditionCheckerFactory;
   private final FlowStateFactory stateFactory = new FlowStateFactory();

   @Inject
   public StepContextFactory(PluginConfigToLogic configToLogic, FlowStepConditionCheckerFactory conditionCheckerFactory) {
      this.configToLogic = configToLogic;
      this.conditionCheckerFactory = conditionCheckerFactory;
   }

   StepContextCache createStepContextCache(List<FlowStepConfig> steps) {
      return new StepContextCache(this.createMap(steps));
   }

   private Map<FlowStepConfig, StepContext> createMap(List<FlowStepConfig> steps) {
      Map<FlowStepConfig, StepContext> map = new IdentityHashMap<>();

      for (FlowStepConfig step : steps) {
         map.put(step, this.stepContext(step));
         if (step instanceof SelectionFlowStepConfig selectionStep) {
            map.putAll(this.createMap(subflowSteps(selectionStep)));
         }
      }

      return map;
   }

   private static List<FlowStepConfig> subflowSteps(SelectionFlowStepConfig step) {
      return sortedOptions(step).stream().map(step::getSubFlowForOption).flatMap(seq -> seq.getSteps().stream()).toList();
   }

   private StepContext stepContext(FlowStepConfig config) {
      Provider<FlowStep> step = CachingDelegationProvider.of(() -> this.configToLogic.getLogic(config));
      Provider<FlowStepConditionChecker> conditionChecker = CachingDelegationProvider.of(() -> this.conditionCheckerFactory.createCheckerForConfig(config));
      return StepContext.builder().config(config).step(step).flowState(this.stateFactory.flowState(step, config)).conditionChecker(conditionChecker).build();
   }

   static List<Option> sortedOptions(SelectionFlowStepConfig selectionStepConfig) {
      return selectionStepConfig.getOptions().stream().sorted(Comparator.comparing(Option::getName)).toList();
   }
}
