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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.airlock.iam.base.misc.exception.Exceptions;
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.domain.model.processor.FlowProcessor;
import com.airlock.iam.flow.domain.model.processor.FlowProcessorRunner;
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.Collection;

public class FlowExceptionHandler {
   private static final ELogger LOG = ELoggerFactory.getLogger(FlowExceptionHandler.class);
   private final Provider<MultiFlowManager> multiFlowManager;
   private final Provider<FlowExitService> flowExitService;
   private final Provider<Collection<FlowProcessor>> flowProcessors;

   @Inject
   public FlowExceptionHandler(
      Provider<MultiFlowManager> multiFlowManager, Provider<FlowExitService> flowExitService, Provider<Collection<FlowProcessor>> flowProcessors
   ) {
      this.multiFlowManager = multiFlowManager;
      this.flowExitService = flowExitService;
      this.flowProcessors = flowProcessors;
   }

   public FailureException toFailureException(RuntimeException e) {
      if (Exceptions.isOrHasCause(e, ServiceConfigurationException.class)) {
         ((MultiFlowManager)this.multiFlowManager.get()).destroyCurrentMultiFlow();
         ServiceConfigurationException serviceConfigurationException = Exceptions.findCause(e, ServiceConfigurationException.class).orElseThrow();
         this.informFlowProcessors(serviceConfigurationException);
         return serviceConfigurationException;
      } else if (Exceptions.isOrHasCause(e, ConfigurationChangeException.class)) {
         ((MultiFlowManager)this.multiFlowManager.get()).destroyCurrentMultiFlow();
         ConfigurationChangeException configurationChangeException = Exceptions.findCause(e, ConfigurationChangeException.class).orElseThrow();
         this.informFlowProcessors(configurationChangeException);
         return configurationChangeException;
      } else {
         this.informFlowProcessors(e);
         ((FlowExitService)this.flowExitService.get()).exitFlow();
         return e instanceof FailureException ? (FailureException)e : new FailureException(e);
      }
   }

   public void informFlowProcessors(RuntimeException e) {
      try {
         FlowProcessorRunner.flowProcessorRunner((Collection<FlowProcessor>)this.flowProcessors.get()).handleException(e);
      } catch (RuntimeException inner) {
         LOG.error(LogTypes.TRACE_MESSAGE, "Exception handler threw unexpected exception: ", inner);
      }
   }
}
