import { describe, expect, it } from "vitest";
import type { SemanticProgramIr } from "../../src/grl/ir/index.js";
import { IrExecutionRuntime, VirtualControllerStateMachine } from "../../src/controller/index.js";

function program(): SemanticProgramIr {
  return {
    moduleName: "Main",
    symbols: [],
    semanticChecks: [],
    paths: [],
    operations: [],
    diagnostics: [],
    sourceMap: [
      {
        kind: "CALL",
        id: "call_helper",
        procedureId: "main",
        sourceMap: { file: "main.grl", line: 2, column: 5 }
      },
      {
        kind: "ALARM",
        id: "helper_alarm",
        procedureId: "helper",
        sourceMap: { file: "main.grl", line: 6, column: 5 }
      }
    ],
    procedures: [
      {
        name: "main",
        sourceMap: { file: "main.grl", line: 1, column: 1 },
        instructions: [
          {
            kind: "RAW_STATEMENT",
            text: "ready = true",
            sourceMap: { file: "main.grl", line: 1, column: 5 }
          },
          {
            kind: "CALL",
            target: "helper",
            args: [],
            sourceMap: { file: "main.grl", line: 2, column: 5 }
          },
          {
            kind: "RETURN",
            sourceMap: { file: "main.grl", line: 3, column: 5 }
          }
        ]
      },
      {
        name: "helper",
        sourceMap: { file: "main.grl", line: 5, column: 1 },
        instructions: [
          {
            kind: "ALARM",
            alarmId: "A1",
            message: "helper alarm",
            severity: "warning",
            sourceMap: { file: "main.grl", line: 6, column: 5 }
          },
          {
            kind: "RETURN",
            sourceMap: { file: "main.grl", line: 7, column: 5 }
          }
        ]
      }
    ],
    kdlBridge: {
      motionRequests: [],
      pathRequests: []
    }
  };
}

describe("virtual controller state machine and IR execution runtime", () => {
  it("reports legal and illegal controller command transitions", () => {
    const machine = new VirtualControllerStateMachine();

    expect(machine.dispatch("run")).toMatchObject({
      ok: false,
      state: "unloaded",
      diagnostic: { code: "VC_INVALID_STATE_TRANSITION" }
    });
    expect(machine.dispatch("load")).toMatchObject({ ok: true, previous: "unloaded", state: "stopped" });
    expect(machine.dispatch("run")).toMatchObject({ ok: true, previous: "stopped", state: "running" });
    expect(machine.dispatch("pause")).toMatchObject({ ok: true, previous: "running", state: "paused" });
    expect(machine.dispatch("step")).toMatchObject({ ok: true, previous: "paused", state: "paused" });
    expect(machine.dispatch("stop")).toMatchObject({ ok: true, previous: "paused", state: "stopped" });
    expect(machine.snapshot().diagnostics).toHaveLength(1);
  });

  it("executes PC, calls, scopes, alarms, trace, source map, and breakpoints", () => {
    const runtime = new IrExecutionRuntime();
    runtime.load(program());

    expect(runtime.snapshot()).toMatchObject({
      procedure: "main",
      pc: 0,
      scopeStack: [{ id: "global" }, { id: "main" }]
    });

    runtime.setBreakpoint({ id: "helper-alarm", procedure: "helper", source: { line: 6 }, enabled: true });
    expect(runtime.step()).toMatchObject({ status: "executed", instruction: { kind: "RAW_STATEMENT" } });
    expect(runtime.getVariable("ready")).toBe(true);
    expect(runtime.step()).toMatchObject({ status: "executed", instruction: { kind: "CALL" } });
    expect(runtime.snapshot()).toMatchObject({
      procedure: "helper",
      pc: 0,
      callStack: [expect.objectContaining({ procedure: "helper", returnTo: { procedure: "main", pc: 2 } })]
    });

    expect(runtime.step()).toMatchObject({ status: "breakpoint", instruction: { kind: "ALARM" } });
    expect(runtime.removeBreakpoint("helper-alarm")).toBe(true);
    expect(runtime.step()).toMatchObject({ status: "executed", instruction: { kind: "ALARM" } });
    expect(runtime.snapshot().alarmQueue).toEqual([
      expect.objectContaining({ id: "A1", message: "helper alarm", severity: "warning" })
    ]);
    expect(runtime.currentSource()).toMatchObject({ sourceMap: { line: 7 } });
    expect(runtime.run()).toMatchObject({ status: "completed" });
    expect(runtime.snapshot().trace).toEqual(
      expect.arrayContaining([
        expect.objectContaining({ kind: "load" }),
        expect.objectContaining({ kind: "breakpoint", data: { breakpointId: "helper-alarm" } }),
        expect.objectContaining({ kind: "alarm", data: { alarmId: "A1", severity: "warning" } })
      ])
    );
  });
});
