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

import com.airlock.iam.flow.api.application.configuration.step.FlowStepConfig;
import com.airlock.iam.flow.api.domain.model.processor.FlowProcessor;
import com.airlock.iam.flow.application.configuration.FlowConfig;
import com.airlock.iam.flow.application.configuration.FlowConfigRepository;
import com.airlock.iam.flow.domain.model.event.GenericStepResultEventFactory;
import com.airlock.iam.flow.domain.model.processor.FlowProcessorFactory;
import com.airlock.iam.flow.domain.model.session.InternalFlowSession;
import com.airlock.iam.flow.domain.model.statemachine.runtime.ConfigurationChangeException;
import com.airlock.iam.flow.domain.model.statemachine.runtime.StateMachineControllerImpl;
import com.airlock.iam.flow.domain.model.statemachine.runtime.StepExceptionHandler;
import com.airlock.iam.flow.domain.model.step.OnFailureGotos;
import com.airlock.iam.flow.domain.model.step.attributes.CustomAttributesAdder;
import com.airlock.iam.flow.domain.model.tag.SuccessTagsHelper;
import com.airlock.iam.flow.domain.service.FlowCompletionService;
import com.airlock.iam.flow.domain.service.FlowExitService;
import com.airlock.iam.flow.domain.service.MultiFlowManager;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;

public final class FlowStateMachineFactory {
   private final Provider<FlowConfigRepository> flowConfigRepository;
   private final Provider<StateMachineFactory> stateMachineFactory;
   private final Provider<FlowProcessorFactory> processorFactory;
   private final Provider<FlowCompletionService> flowCompletionService;
   private final Provider<FlowExitService> flowExitService;
   private final Provider<StepExceptionHandler> stepExceptionHandler;
   private final Provider<SuccessTagsHelper> successTags;
   private final Provider<GenericStepResultEventFactory> eventFactory;
   private final Provider<CustomAttributesAdder> customAttributesAdder;
   private final Provider<OnFailureGotos> onFailureGotos;

   @Inject
   public FlowStateMachineFactory(
      Provider<FlowConfigRepository> flowConfigRepository,
      Provider<StateMachineFactory> stateMachineFactory,
      Provider<FlowProcessorFactory> processorFactory,
      Provider<FlowCompletionService> flowCompletionService,
      Provider<FlowExitService> flowExitService,
      Provider<StepExceptionHandler> stepExceptionHandler,
      Provider<SuccessTagsHelper> successTags,
      Provider<GenericStepResultEventFactory> eventFactory,
      Provider<CustomAttributesAdder> customAttributesAdder,
      Provider<OnFailureGotos> onFailureGotos
   ) {
      this.flowConfigRepository = flowConfigRepository;
      this.stateMachineFactory = stateMachineFactory;
      this.processorFactory = processorFactory;
      this.flowCompletionService = flowCompletionService;
      this.flowExitService = flowExitService;
      this.stepExceptionHandler = stepExceptionHandler;
      this.successTags = successTags;
      this.eventFactory = eventFactory;
      this.customAttributesAdder = customAttributesAdder;
      this.onFailureGotos = onFailureGotos;
   }

   public FlowStateMachine createEmptyStateMachine(InternalFlowSession flowSession, MultiFlowManager multiFlowManager) {
      FlowConfig flowConfig = ((FlowConfigRepository)this.flowConfigRepository.get())
         .getFlowConfigByConfigHash(flowSession.getFlowConfigHash())
         .orElseThrow(
            () -> {
               multiFlowManager.destroyCurrentMultiFlow();
               return new ConfigurationChangeException(
                  String.format(
                     "Destroying flow session due to a possible configuration change. FlowConfigHash '%s' was not found.", flowSession.getFlowConfigHash()
                  )
               );
            }
         );
      List<FlowProcessor> flowProcessors = ((FlowProcessorFactory)this.processorFactory.get()).getProcessorsForConfig(flowConfig.getProcessors());
      List<FlowStepConfig> flowStepConfigs = flowConfig.getSteps().getSteps();
      StateMachineController controller = new StateMachineControllerImpl(
         multiFlowManager,
         flowProcessors,
         (SuccessTagsHelper)this.successTags.get(),
         (FlowCompletionService)this.flowCompletionService.get(),
         (FlowExitService)this.flowExitService.get(),
         (StepExceptionHandler)this.stepExceptionHandler.get(),
         flowSession,
         this.eventFactory,
         this.customAttributesAdder,
         this.onFailureGotos
      );
      return ((StateMachineFactory)this.stateMachineFactory.get()).create(flowStepConfigs, controller);
   }
}
