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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.domain.model.event.StateMachineFlowEvent;
import com.airlock.iam.flow.domain.model.statemachine.FlowState;
import com.airlock.iam.flow.domain.model.statemachine.FlowStateMachine;
import com.airlock.iam.flow.domain.model.statemachine.spring.SpringFlowStateMachine;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;

public final class StateMachineRestore {
   private static final ELogger LOG = ELoggerFactory.getLogger(StateMachineRestore.class);
   private final Provider<StateMachinePersister<FlowState, StateMachineFlowEvent, SerializedStateMachineContext>> persister;

   @Inject
   public StateMachineRestore(Provider<StateMachinePersister<FlowState, StateMachineFlowEvent, SerializedStateMachineContext>> persister) {
      this.persister = persister;
   }

   public SerializedStateMachineContext serializedStateMachineContext(FlowStateMachine stateMachine) {
      StopWatch watch = StopWatch.createStarted();
      SerializedStateMachineContext serializedContext = this.applyPersist(toSpringStateMachine(stateMachine));
      LOG.debug("State machine successfully persisted in " + LogUtil.formatNanos(watch.getNanoTime()));
      return serializedContext;
   }

   public void initializeStateMachineFromContext(FlowStateMachine stateMachine, SerializedStateMachineContext serialized) {
      StopWatch watch = StopWatch.createStarted();
      this.applyRestore(stateMachine, serialized);
      LOG.debug("State machine successfully restored in " + LogUtil.formatNanos(watch.getNanoTime()));
   }

   private SerializedStateMachineContext applyPersist(StateMachine<FlowState, StateMachineFlowEvent> stateMachine) {
      try {
         SerializedStateMachineContext serialized = new SerializedStateMachineContext();
         ((StateMachinePersister)this.persister.get()).persist(stateMachine, serialized);
         return serialized;
      } catch (Exception e) {
         throw new FailureException("Could not save the statemachine", e);
      }
   }

   private void applyRestore(FlowStateMachine stateMachine, SerializedStateMachineContext serialized) {
      try {
         ((StateMachinePersister)this.persister.get()).restore(toSpringStateMachine(stateMachine), serialized);
      } catch (Exception e) {
         throw new FailureException("Could not restore the statemachine", e);
      }
   }

   private static StateMachine<FlowState, StateMachineFlowEvent> toSpringStateMachine(FlowStateMachine stateMachine) {
      return ((SpringFlowStateMachine)stateMachine).getStateMachine();
   }
}
