package p.studio.compiler.workspaces.stages;

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

@Slf4j
public class EmitBytecodePipelineStage implements PipelineStage {
    private final BytecodeEmitter emitter;

    public EmitBytecodePipelineStage() {
        this(new BytecodeEmitter());
    }

    EmitBytecodePipelineStage(final BytecodeEmitter emitter) {
        this.emitter = emitter;
    }

    @Override
    public BuildingIssueSink run(BuilderPipelineContext ctx, LogAggregator logs) {
        if (ctx.optimizedIrvm == null) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_EMIT_BYTECODE")
                            .code("MARSHAL_FORMAT_STAGE_INPUT_MISSING")
                            .message("[BUILD]: optimized IRVM is missing before EmitBytecode stage"));
        }
        if (ctx.optimizedIrvm.hasInternalOpcodes()) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_EMIT_BYTECODE")
                            .code("MARSHAL_VERIFY_PRECHECK_INTERNAL_OPCODE_RESIDUAL")
                            .message("[BUILD]: optimized IRVM still contains internal opcodes"));
        }
        try {
            ctx.bytecodeModule = emitter.emit(ctx.optimizedIrvm.coherentEmissionPlan());
            ctx.bytecodeBytes = ctx.bytecodeModule.serialize();
        } catch (IllegalArgumentException e) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_EMIT_BYTECODE")
                            .code("MARSHAL_VERIFY_PRECHECK_IRVM_PROGRAM_COHERENCE")
                            .message("[BUILD]: irvm program coherence failed: " + e.getMessage())
                            .exception(e));
        } catch (BytecodeMarshalingException e) {
            return BuildingIssueSink.empty()
                    .report(builder -> builder
                            .error(true)
                            .phase("BACKEND_EMIT_BYTECODE")
                            .code(e.code().name())
                            .message("[BUILD]: bytecode marshaling failed: " + e.getMessage())
                            .exception(e));
        }
        return BuildingIssueSink.empty();
    }
}
