package click.kamil.springstatemachineexporter.analysis.resolver;

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

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

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

class TruncatedEnumRefReconstructorTest {

    @Test
    void shouldReconstructPackageTruncatedConstantWhenUnique(@TempDir Path tempDir) throws IOException {
        CodebaseContext context = scanOrderAndDoc(tempDir, false);

        assertThat(TruncatedEnumRefReconstructor.reconstruct("com.example.PAY", context))
                .isEqualTo("com.example.OrderEvent.PAY");
        assertThat(TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef("com.example.PAY"))
                .isTrue();
        assertThat(TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef("com.example.OrderEvent.PAY"))
                .isFalse();
        assertThat(TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef("order.pay"))
                .isFalse();
    }

    @Test
    void shouldUsePreferredTypeWhenConstantIsAmbiguousAcrossEnums(@TempDir Path tempDir) throws IOException {
        CodebaseContext context = scanOrderAndDoc(tempDir, true);

        // Both OrderEvent and DocumentEvent have PAY under com.example
        assertThat(TruncatedEnumRefReconstructor.reconstruct("com.example.PAY", context))
                .isEqualTo("com.example.PAY");
        assertThat(TruncatedEnumRefReconstructor.reconstruct(
                "com.example.PAY", "com.example.OrderEvent", context))
                .isEqualTo("com.example.OrderEvent.PAY");
        assertThat(TruncatedEnumRefReconstructor.reconstruct(
                "com.example.PAY", "com.example.DocumentEvent", context))
                .isEqualTo("com.example.DocumentEvent.PAY");
    }

    @Test
    void shouldNotInventConstantForTrailingDotTypeRef(@TempDir Path tempDir) throws IOException {
        CodebaseContext context = scanOrderAndDoc(tempDir, false);

        assertThat(TruncatedEnumRefReconstructor.reconstruct("com.example.OrderEvent.", context))
                .isEqualTo("com.example.OrderEvent.");
        assertThat(TruncatedEnumRefReconstructor.reconstruct(
                "com.example.OrderEvent.", "com.example.OrderEvent", context))
                .isEqualTo("com.example.OrderEvent.");
    }

    @Test
    void shouldCanonicalizeTruncatedPolyEventViaMachineEnumCanonicalizer(@TempDir Path tempDir) throws IOException {
        CodebaseContext context = scanOrderAndDoc(tempDir, false);

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

    private static CodebaseContext scanOrderAndDoc(Path tempDir, boolean documentAlsoHasPay) throws IOException {
        Path dir = tempDir.resolve("com/example");
        Files.createDirectories(dir);
        Files.writeString(dir.resolve("OrderEvent.java"),
                "package com.example;\npublic enum OrderEvent { PAY, SHIP }\n");
        String docConstants = documentAlsoHasPay ? "PAY, ARCHIVE" : "ARCHIVE";
        Files.writeString(dir.resolve("DocumentEvent.java"),
                "package com.example;\npublic enum DocumentEvent { " + docConstants + " }\n");

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