package p.studio.compiler.workspaces.phases;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.studio.compiler.messages.DependencyConfig;
import p.studio.compiler.models.DependencyContext;
import p.studio.compiler.models.ProjectDescriptor;
import p.studio.compiler.models.ProjectInfo;
import p.studio.compiler.models.PrometeuManifest;
import p.studio.compiler.source.identifiers.ProjectId;
import p.studio.utilities.structures.ReadOnlyList;

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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class StackPhaseTest {

    @TempDir
    Path tempDir;

    @Test
    void shouldBuildDeterministicTopologicalOrder() {
        final var ctx = DependencyContext.basedOn(new DependencyConfig(false, tempDir));
        registerProject(ctx, "app");
        registerProject(ctx, "core");
        registerProject(ctx, "util");

        ctx.dependenciesByProject.add(List.of(new ProjectId(1), new ProjectId(2)));
        ctx.dependenciesByProject.add(List.of(new ProjectId(2)));
        ctx.dependenciesByProject.add(List.of());

        final var issues = new StackPhase().run(ctx);

        assertTrue(!issues.hasErrors());
        assertEquals(ReadOnlyList.wrap(List.of(new ProjectId(2), new ProjectId(1), new ProjectId(0))), ctx.stack.topologicalOrder);
    }

    @Test
    void shouldReportCycles() {
        final var ctx = DependencyContext.basedOn(new DependencyConfig(false, tempDir));
        registerProject(ctx, "a");
        registerProject(ctx, "b");
        registerProject(ctx, "c");

        ctx.dependenciesByProject.add(List.of(new ProjectId(1)));
        ctx.dependenciesByProject.add(List.of(new ProjectId(2)));
        ctx.dependenciesByProject.add(List.of(new ProjectId(0)));

        final var issues = new StackPhase().run(ctx);

        assertTrue(issues.hasErrors());
        assertTrue(issues.asCollection().stream().anyMatch(issue -> issue.getMessage().contains("a -> b -> c")));
    }

    private void registerProject(final DependencyContext ctx, final String name) {
        final var root = tempDir.resolve(name);
        ctx.projectTable.register(ProjectDescriptor.builder()
                .rootPath(root)
                .name(name)
                .version("1.0.0")
                .sourceRoots(ReadOnlyList.wrap(List.of()))
                .build());
        ctx.projectInfoTable.register(ProjectInfo.builder()
                .rootDirectory(root)
                .manifestPath(root.resolve("prometeu.json"))
                .manifest(new PrometeuManifest(name, "1.0.0", "pbs", 1, ReadOnlyList.wrap(List.of())))
                .build());
    }
}
