package p.studio.compiler.workspaces.stages;

import org.junit.jupiter.api.Test;
import p.studio.compiler.backend.irvm.*;
import p.studio.compiler.messages.BuilderPipelineConfig;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.utilities.logs.LogAggregator;
import p.studio.utilities.structures.ReadOnlyList;

import static org.junit.jupiter.api.Assertions.*;

class OptimizeIRVMPipelineStageTest {

    @Test
    void runMustFailWhenIrvmIsMissing() {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        final var stage = new OptimizeIRVMPipelineStage();

        final var issues = stage.run(ctx, LogAggregator.empty());
        final var firstIssue = issues.asCollection().iterator().next();

        assertTrue(issues.hasErrors());
        assertEquals(1, issues.size());
        assertEquals("BACKEND_OPTIMIZE_IRVM", firstIssue.getPhase());
        assertEquals("MARSHAL_FORMAT_STAGE_INPUT_MISSING", firstIssue.getCode());
    }

    @Test
    void runMustProduceNoopOptimizedProgram() {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        ctx.irvm = new IRVMProgram(new IRVMModule(
                "core-v1",
                ReadOnlyList.from(new IRVMFunction(
                        "main",
                        0,
                        0,
                        0,
                        1,
                        ReadOnlyList.from(
                                new IRVMInstruction(IRVMOp.HALT, null))))));
        final var stage = new OptimizeIRVMPipelineStage();

        final var issues = stage.run(ctx, LogAggregator.empty());

        assertFalse(issues.hasErrors());
        assertNotNull(ctx.optimizedIrvm);
        assertEquals(ctx.irvm, ctx.optimizedIrvm);
    }

    @Test
    void runMustRejectUnsupportedVmProfile() {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        ctx.irvm = new IRVMProgram(new IRVMModule(
                "experimental",
                ReadOnlyList.from(new IRVMFunction(
                        "main",
                        0,
                        0,
                        0,
                        1,
                        ReadOnlyList.from(
                                new IRVMInstruction(IRVMOp.HALT, null))))));
        final var stage = new OptimizeIRVMPipelineStage();

        final var issues = stage.run(ctx, LogAggregator.empty());

        assertTrue(issues.hasErrors());
        assertEquals(1, issues.size());
    }

    @Test
    void runMustAcceptSupportedNonDefaultVmProfile() {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, ".", "experimental-v1"));
        ctx.irvm = new IRVMProgram(new IRVMModule(
                "experimental-v1",
                ReadOnlyList.from(new IRVMFunction(
                        "main",
                        0,
                        0,
                        0,
                        1,
                        ReadOnlyList.from(
                                new IRVMInstruction(IRVMOp.HALT, null))))));
        final var stage = new OptimizeIRVMPipelineStage();

        final var issues = stage.run(ctx, LogAggregator.empty());

        assertFalse(issues.hasErrors());
        assertNotNull(ctx.optimizedIrvm);
        assertEquals("experimental-v1", ctx.optimizedIrvm.module().vmProfile());
    }
}
