package p.studio.compiler.workspaces.stages;

import org.junit.jupiter.api.Test;
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.ByteBuffer;
import java.nio.ByteOrder;

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

class LinkBytecodePipelineStageTest {

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

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

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

    @Test
    void runMustReportHostcallIndexPrecheckFailure() {
        final var ctx = BuilderPipelineContext.fromConfig(new BuilderPipelineConfig(false, "."));
        ctx.bytecodeModule = new BytecodeModule(
                0,
                ReadOnlyList.empty(),
                ReadOnlyList.from(new BytecodeModule.FunctionMeta(0, 8, 0, 0, 0, 1)),
                codeHostcall(1),
                null,
                ReadOnlyList.empty(),
                ReadOnlyList.empty());

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

        assertTrue(issues.hasErrors());
        assertEquals("MARSHAL_VERIFY_PRECHECK_HOSTCALL_INDEX_INVALID", firstIssue.getCode());
    }

    private byte[] codeHostcall(final int index) {
        final var out = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
        out.putShort((short) 0x71);
        out.putInt(index);
        out.putShort((short) 0x51);
        return out.array();
    }
}
