package p.studio.compiler.workspaces.stages;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.studio.compiler.backend.bytecode.BytecodeModule;
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 java.nio.file.Files;
import java.nio.file.Path;

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

class WriteBytecodeArtifactPipelineStageTest {

    @TempDir
    Path tempDir;

    @Test
    void runMustFailWhenRootProjectPathIsMissing() {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        ctx.bytecodeBytes = new byte[] { 0x01 };

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

        assertTrue(issues.hasErrors());
        assertEquals("BACKEND_WRITE_ARTIFACT", firstIssue.getPhase());
        assertEquals("MARSHAL_FORMAT_STAGE_INPUT_MISSING", firstIssue.getCode());
    }

    @Test
    void runMustFailWhenBytecodeInputIsMissing() {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        ctx.rootProjectPathCanon = tempDir;

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

        assertTrue(issues.hasErrors());
        assertEquals("BACKEND_WRITE_ARTIFACT", firstIssue.getPhase());
        assertEquals("MARSHAL_FORMAT_STAGE_INPUT_MISSING", firstIssue.getCode());
    }

    @Test
    void runMustWriteBuildProgramPbxFromByteArray() throws Exception {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        ctx.rootProjectPathCanon = tempDir;
        ctx.bytecodeBytes = new byte[] { 0x50, 0x42, 0x58, 0x00 };

        final var issues = new WriteBytecodeArtifactPipelineStage().run(ctx, LogAggregator.empty());
        final var outputPath = tempDir.resolve("build").resolve("program.pbx");

        assertFalse(issues.hasErrors());
        assertTrue(Files.exists(outputPath));
        assertArrayEquals(ctx.bytecodeBytes, Files.readAllBytes(outputPath));
        assertEquals(outputPath, ctx.bytecodeArtifactPath);
    }

    @Test
    void runMustWriteBuildProgramPbxFromModuleWhenByteArrayIsMissing() throws Exception {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        ctx.rootProjectPathCanon = tempDir;
        ctx.bytecodeModule = new BytecodeModule(
                0,
                ReadOnlyList.empty(),
                ReadOnlyList.empty(),
                new byte[] { (byte) 0x51, 0x00 },
                null,
                ReadOnlyList.empty(),
                ReadOnlyList.empty());

        final var issues = new WriteBytecodeArtifactPipelineStage().run(ctx, LogAggregator.empty());
        final var outputPath = tempDir.resolve("build").resolve("program.pbx");

        assertFalse(issues.hasErrors());
        assertTrue(Files.exists(outputPath));
        assertArrayEquals(ctx.bytecodeModule.serialize(), Files.readAllBytes(outputPath));
        assertEquals(outputPath, ctx.bytecodeArtifactPath);
    }
}
