package click.kamil.examples.statemachine.enumstate;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;

@Configuration
@EnableStateMachineFactory(name = "kamilEnumStateMachineFactory")
@Slf4j
class KamilEnumStateMachineConfiguration extends StateMachineConfigurerAdapter<OrderStates, OrderEvents> {
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
        Guard<OrderStates, OrderEvents> guard1 = null;
        Action<OrderStates, OrderEvents> action2 = null;
        transitions
                .withExternal().source(OrderStates.SUBMITTED).target(OrderStates.PAID).event(OrderEvents.PAY)
                .and()
                .withExternal().source(OrderStates.PAID).target(OrderStates.FULFILLED).event(OrderEvents.FULFILL)
                .and()
                .withExternal().source(OrderStates.SUBMITTED).target(OrderStates.CANCELED).event(OrderEvents.CANCEL).guard(new Guard<OrderStates, OrderEvents>() {
                    @Override
                    public boolean evaluate(StateContext<OrderStates, OrderEvents> context) {
                        return false;
                    }
                })
                .and()
                .withExternal().source(OrderStates.PAID).target(OrderStates.CANCELED).event(OrderEvents.CANCEL)

                .and()
                .withExternal().source(OrderStates.SUBMITTED).event(OrderEvents.ABCD)
                // it needs to have target specified to get added to .transitions collections
                .and()
                .withExternal().source(OrderStates.SUBMITTED).target(OrderStates.PAID1).event(OrderEvents.ABCD)
//
                .and()
                .withChoice().source(OrderStates.PAID1)
                .first(OrderStates.PAID2, new Guard<OrderStates, OrderEvents>() {
                    @Override
                    public boolean evaluate(StateContext<OrderStates, OrderEvents> context) {
                        return true;
                    }
                })
                .then(OrderStates.PAID3, guard1)
                .then(OrderStates.HAPPEN, new Guard<OrderStates, OrderEvents>() {
                    @Override
                    public boolean evaluate(StateContext<OrderStates, OrderEvents> context) {
                        return false;
                    }
                })
                .last(OrderStates.CANCELED)
                .and().withExternal().source(OrderStates.PAID2).target(OrderStates.CANCELED)
                .and().withExternal().source(OrderStates.PAID3).target(OrderStates.CANCELED)
//				.and().withLocal().source(OrderStates.SUBMITTED).target(OrderStates.CANCELED)
                .and().withExternal().source(OrderStates.PAID2).event(OrderEvents.ABCD).action(new Action<OrderStates, OrderEvents>() {
                    @Override
                    public void execute(StateContext<OrderStates, OrderEvents> context) {
                    }
                }).action(action2);
    }

    @Override
    public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
        states.withStates()
                .initial(OrderStates.SUBMITTED)
                .state(OrderStates.PAID)
                .state(OrderStates.PAID2)
                .state(OrderStates.PAID3)
                .end(OrderStates.FULFILLED)
                .end(OrderStates.CANCELED);
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config) throws Exception {
        StateMachineListenerAdapter<OrderStates, OrderEvents> listenerAdapter = new StateMachineListenerAdapter<>() {
            @Override
            public void stateChanged(State<OrderStates, OrderEvents> from, State<OrderStates, OrderEvents> to) {
                log.info("state changed from {} to {}", from, to);
            }
        };
        config.withConfiguration()
                .autoStartup(false)
                .listener(listenerAdapter);
    }
}
