package click.kamil.springstatemachineexporter.ast.app;

import click.kamil.springstatemachineexporter.model.State;
import click.kamil.springstatemachineexporter.model.Transition;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;

class TransitionStateUtilsTest {

    @Test
    void shouldFindStartStates_whenStatesHaveNoIncomingTransitions() {
        List<Transition> transitions = List.of(
                createTransition("CREATED", "PAID"),
                createTransition("PAID", "SHIPPED"),
                createTransition("SHIPPED", "COMPLETED")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);

        assertThat(startStates).containsExactly("CREATED");
    }

    @Test
    void shouldFindStartStates_whenMultipleStatesHaveNoIncomingTransitions() {
        List<Transition> transitions = List.of(
                createTransition("CREATED", "PAID"),
                createTransition("PENDING", "PAID"),
                createTransition("PAID", "SHIPPED")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);

        assertThat(startStates).containsExactlyInAnyOrder("CREATED", "PENDING");
    }

    @Test
    void shouldFindEndStates_whenStatesHaveNoOutgoingTransitions() {
        List<Transition> transitions = List.of(
                createTransition("CREATED", "PAID"),
                createTransition("PAID", "SHIPPED"),
                createTransition("PAID", "CANCELLED")
        );

        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(endStates).containsExactlyInAnyOrder("SHIPPED", "CANCELLED");
    }

    @Test
    void shouldFindEndStates_whenMultipleStatesHaveNoOutgoingTransitions() {
        List<Transition> transitions = List.of(
                createTransition("CREATED", "PAID"),
                createTransition("PAID", "SHIPPED"),
                createTransition("PAID", "CANCELLED"),
                createTransition("PAID", "REFUNDED")
        );

        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(endStates).containsExactlyInAnyOrder("SHIPPED", "CANCELLED", "REFUNDED");
    }

    @Test
    void shouldHandleEmptyTransitionsList() {
        Set<String> startStates = TransitionStateUtils.findStartStates(List.of());
        Set<String> endStates = TransitionStateUtils.findEndStates(List.of());

        assertThat(startStates).isEmpty();
        assertThat(endStates).isEmpty();
    }

    @Test
    void shouldHandleTransitionsWithQuotedStates() {
        List<Transition> transitions = List.of(
                createTransition("\"CREATED\"", "\"PAID\""),
                createTransition("\"PAID\"", "\"SHIPPED\"")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).containsExactly("CREATED");
        assertThat(endStates).containsExactly("SHIPPED");
    }

    @Test
    void shouldHandleTransitionsWithMixedQuotedAndUnquotedStates() {
        List<Transition> transitions = List.of(
                createTransition("CREATED", "\"PAID\""),
                createTransition("\"PAID\"", "SHIPPED")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).containsExactly("CREATED");
        assertThat(endStates).containsExactly("SHIPPED");
    }

    @Test
    void shouldHandleTransitionsWithNullStates() {
        List<Transition> transitions = List.of(
                createTransition(null, "PAID"),
                createTransition("PAID", null)
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).isEmpty();
        assertThat(endStates).isEmpty();
    }

    @Test
    void shouldHandleTransitionsWithEmptyStates() {
        List<Transition> transitions = List.of(
                createTransition("", "PAID"),
                createTransition("PAID", "")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).isEmpty();
        assertThat(endStates).isEmpty();
    }

    @Test
    void shouldHandleTransitionsWithMultipleSourceStates() {
        Transition transition1 = createTransition(List.of("CREATED", "PENDING"), List.of("PAID"));
        Transition transition2 = createTransition(List.of("PAID"), List.of("SHIPPED"));
        List<Transition> transitions = List.of(transition1, transition2);

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).containsExactlyInAnyOrder("CREATED", "PENDING");
        assertThat(endStates).containsExactly("SHIPPED");
    }

    @Test
    void shouldHandleTransitionsWithMultipleTargetStates() {
        Transition transition1 = createTransition(
                List.of("PAID"),
                List.of("SHIPPED", "CANCELLED")
        );
        List<Transition> transitions = List.of(transition1);

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).containsExactly("PAID");
        assertThat(endStates).containsExactlyInAnyOrder("SHIPPED", "CANCELLED");
    }

    @Test
    void shouldHandleCircularTransitions() {
        List<Transition> transitions = List.of(
                createTransition("A", "B"),
                createTransition("B", "C"),
                createTransition("C", "A")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).isEmpty();
        assertThat(endStates).isEmpty();
    }

    @Test
    void shouldHandleSelfLoopingTransitions() {
        List<Transition> transitions = List.of(
                createTransition("A", "A"),
                createTransition("A", "B")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).isEmpty(); // A is both source and target, so not a start state
        assertThat(endStates).containsExactly("B"); // Only B is a pure end state
    }

    @Test
    void shouldHandleVariousStateIdFormats() {
        List<Transition> transitions = List.of(
                createTransition(" OrderStates.START ", "STATE_1"),
                createTransition("STATE_1", "getEndState()")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).containsExactly(" OrderStates.START ");
        assertThat(endStates).containsExactly("getEndState()");
    }

    @Test
    void shouldHandleComplexStateMachine() {
        List<Transition> transitions = List.of(
                createTransition("CREATED", "PAID"),
                createTransition("PAID", "SHIPPED"),
                createTransition("PAID", "CANCELLED"),
                createTransition("SHIPPED", "DELIVERED"),
                createTransition("CANCELLED", "REFUNDED")
        );

        Set<String> startStates = TransitionStateUtils.findStartStates(transitions);
        Set<String> endStates = TransitionStateUtils.findEndStates(transitions);

        assertThat(startStates).containsExactly("CREATED");
        assertThat(endStates).containsExactlyInAnyOrder("DELIVERED", "REFUNDED");
    }

    @Test
    void shouldDeduplicateStatesByFullIdentifier() {
        Transition transition = new Transition();
        transition.getSourceStates().add(State.of("\"NEW\"", "String.NEW"));
        transition.getTargetStates().add(State.of("\"PAID\"", "String.PAID"));

        Set<State> allStates = TransitionStateUtils.findAllStates(
                List.of(transition),
                Set.of("String.NEW"),
                Set.of("String.PAID"));

        assertThat(allStates).hasSize(2);
        assertThat(allStates).extracting(State::fullIdentifier)
                .containsExactlyInAnyOrder("String.NEW", "String.PAID");
        assertThat(allStates).extracting(State::rawName)
                .containsExactlyInAnyOrder("\"NEW\"", "\"PAID\"");
    }

    private Transition createTransition(String source, String target) {
        Transition transition = new Transition();
        if (source != null) {
            transition.getSourceStates().add(State.of(source));
        }
        if (target != null) {
            transition.getTargetStates().add(State.of(target));
        }
        return transition;
    }

    private Transition createTransition(List<String> sources, List<String> targets) {
        Transition transition = new Transition();
        if (sources != null) {
            for (String s : sources)
                transition.getSourceStates().add(State.of(s));
        }
        if (targets != null) {
            for (String t : targets)
                transition.getTargetStates().add(State.of(t));
        }
        return transition;
    }
} 
