package p.studio.compiler.workspaces.stages;

import lombok.extern.slf4j.Slf4j;
import p.studio.compiler.messages.BuildingIssueSink;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.workspaces.PipelineStage;
import p.studio.utilities.logs.LogAggregator;

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

@Slf4j
public class WriteBytecodeArtifactPipelineStage implements PipelineStage {
    private static final String OUTPUT_DIR = "build";
    private static final String OUTPUT_FILE = "program.pbx";

    @Override
    public BuildingIssueSink run(final BuilderPipelineContext ctx, final LogAggregator logs) {
        if (ctx.rootProjectPathCanon == null) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_WRITE_ARTIFACT")
                            .code("MARSHAL_FORMAT_STAGE_INPUT_MISSING")
                            .message("[BUILD]: root project path is missing before WriteBytecodeArtifact stage"));
        }

        final var bytes = resolveBytes(ctx);
        if (bytes == null || bytes.length == 0) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_WRITE_ARTIFACT")
                            .code("MARSHAL_FORMAT_STAGE_INPUT_MISSING")
                            .message("[BUILD]: bytecode bytes are missing before WriteBytecodeArtifact stage"));
        }

        final var outputDir = ctx.rootProjectPathCanon.resolve(OUTPUT_DIR);
        final var outputPath = outputDir.resolve(OUTPUT_FILE);
        try {
            Files.createDirectories(outputDir);
            Files.write(
                    outputPath,
                    bytes,
                    StandardOpenOption.CREATE,
                    StandardOpenOption.TRUNCATE_EXISTING,
                    StandardOpenOption.WRITE);
            ctx.bytecodeArtifactPath = outputPath;
            logs.using(log).info("[BUILD]: wrote bytecode artifact to " + outputPath);
            return BuildingIssueSink.empty();
        } catch (IOException e) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_WRITE_ARTIFACT")
                            .code("MARSHAL_ARTIFACT_WRITE_FAILED")
                            .message("[BUILD]: failed to write bytecode artifact: " + outputPath)
                            .exception(e));
        }
    }

    private byte[] resolveBytes(final BuilderPipelineContext ctx) {
        if (ctx.bytecodeBytes != null && ctx.bytecodeBytes.length > 0) {
            return ctx.bytecodeBytes;
        }
        if (ctx.bytecodeModule != null) {
            return ctx.bytecodeModule.serialize();
        }
        return null;
    }
}
