package click.kamil.springstatemachineexporter.analysis.resolver;

import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

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

class MachineEnumCanonicalizerCrossPackageTest {

    @Test
    void shouldNotRewriteCrossPackageEnumReferencesWhenSimpleNamesMatch() {
        String machineEventType = "com.bar.OrderEvents";
        String foreignEvent = "com.foo.OrderEvents.PAY";

        assertThat(MachineEnumCanonicalizer.enumTypesMatch(machineEventType, "com.foo.OrderEvents"))
                .isFalse();
        assertThat(MachineEnumCanonicalizer.canonicalizeLabel(foreignEvent, machineEventType))
                .isEqualTo(foreignEvent);
    }

    @Test
    void shouldCanonicalizeImportStyleReferencesForMachineEnumType() {
        assertThat(MachineEnumCanonicalizer.canonicalizeLabel(
                "OrderEvents.PAY", "com.bar.OrderEvents"))
                .isEqualTo("com.bar.OrderEvents.PAY");
    }

    @Test
    void shouldCanonicalizeImportStyleWhenMachineEnumTypeIsKnown(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("a"));
        Files.createDirectories(tempDir.resolve("b"));
        Files.writeString(tempDir.resolve("a/OrderEvent.java"),
                "package a; public enum OrderEvent { PAY }");
        Files.writeString(tempDir.resolve("b/OrderEvent.java"),
                "package b; public enum OrderEvent { CANCEL }");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        assertThat(MachineEnumCanonicalizer.canonicalizeLabel("OrderEvent.PAY", "a.OrderEvent", context))
                .isEqualTo("a.OrderEvent.PAY");
    }

    @Test
    void shouldNotCapImportStyleWhenSimpleNameIsAmbiguous(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("a"));
        Files.createDirectories(tempDir.resolve("b"));
        Files.writeString(tempDir.resolve("a/OrderEvent.java"),
                "package a; public enum OrderEvent { PAY }");
        Files.writeString(tempDir.resolve("b/OrderEvent.java"),
                "package b; public enum OrderEvent { CANCEL }");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        assertThat(MachineEnumCanonicalizer.capToConfiguredTransitionEvents(
                List.of("OrderEvent.PAY"),
                List.of("a.OrderEvent.PAY"),
                "a.OrderEvent",
                context)).isEmpty();
    }

    @Test
    void shouldCanonicalizeLabelsForLinkingWithoutOverwritingTypeFqns(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("com/example/order"));
        Files.writeString(tempDir.resolve("com/example/order/OrderEvent.java"),
                "package com.example.order; public enum OrderEvent { PAY, SHIP }");
        Files.writeString(tempDir.resolve("App.java"), "package com.example; class App {}");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        TriggerPoint trigger = TriggerPoint.builder()
                .event("OrderEvent.valueOf(action)")
                .polymorphicEvents(List.of("OrderEvent.PAY", "OrderEvent.SHIP"))
                .build();
        StateMachineTypeResolver.MachineTypes types = new StateMachineTypeResolver.MachineTypes(
                "com.example.order.OrderState", "com.example.order.OrderEvent");

        TriggerPoint linked = MachineEnumCanonicalizer.canonicalizeTriggerLabelsForLinking(
                trigger, types, context);

        assertThat(linked.getEventTypeFqn()).isNull();
        assertThat(linked.getStateTypeFqn()).isNull();
        assertThat(linked.getPolymorphicEvents()).containsExactly(
                "com.example.order.OrderEvent.PAY",
                "com.example.order.OrderEvent.SHIP");
    }

    @Test
    void shouldNotQualifyAmbiguousImportStyleEventReference(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("a"));
        Files.createDirectories(tempDir.resolve("b"));
        Files.writeString(tempDir.resolve("a/OrderEvent.java"),
                "package a; public enum OrderEvent { PAY }");
        Files.writeString(tempDir.resolve("b/OrderEvent.java"),
                "package b; public enum OrderEvent { CANCEL }");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        assertThat(MachineEnumCanonicalizer.qualifyEventIdentifier(
                "OrderEvent.PAY", "a.OrderEvent", context)).isEqualTo("OrderEvent.PAY");
        assertThat(MachineEnumCanonicalizer.isMachineEnumReference(
                "OrderEvent.PAY", "a.OrderEvent", context)).isFalse();
    }

    @Test
    void shouldCanonicalizeAmbiguousImportStyleTransitionEventsWhenMachineTypeKnown(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("a"));
        Files.createDirectories(tempDir.resolve("b"));
        Files.writeString(tempDir.resolve("a/OrderEvent.java"),
                "package a; public enum OrderEvent { PAY }");
        Files.writeString(tempDir.resolve("b/OrderEvent.java"),
                "package b; public enum OrderEvent { CANCEL }");
        Files.writeString(tempDir.resolve("a/OrderState.java"),
                "package a; public enum OrderState { NEW, PAID }");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        click.kamil.springstatemachineexporter.model.Transition transition =
                new click.kamil.springstatemachineexporter.model.Transition();
        transition.setEvent(click.kamil.springstatemachineexporter.model.Event.of(
                "OrderEvent.PAY", "OrderEvent.PAY"));
        transition.setSourceStates(List.of(click.kamil.springstatemachineexporter.model.State.of(
                "OrderState.NEW", "OrderState.NEW")));
        transition.setTargetStates(List.of(click.kamil.springstatemachineexporter.model.State.of(
                "OrderState.PAID", "OrderState.PAID")));

        StateMachineTypeResolver.MachineTypes types = new StateMachineTypeResolver.MachineTypes(
                "a.OrderState", "a.OrderEvent");
        MachineEnumCanonicalizer.canonicalizeTransitions(List.of(transition), types, context);

        assertThat(transition.getEvent().fullIdentifier()).isEqualTo("a.OrderEvent.PAY");
        assertThat(transition.getSourceStates().get(0).fullIdentifier()).isEqualTo("a.OrderState.NEW");
    }
}
