import { describe, expect, it } from "vitest";
import type { GrlProcedureDeclaration, GrlRawTopLevelDeclaration } from "../../src/grl/ast/index.js";
import { parseGrl } from "../../src/grl/parser/index.js";
import {
  analyzeExceptionSemantics,
  parseProcedureExceptionFlow
} from "../../src/grl/semantic/index.js";

const PROGRAM = `language grl 0.1
module Main
  trap recover_trap()
    raise E_STOP
  end
  task background cycle 10 ms
    call monitor()
  end
  proc main()
    alarm E_STOP "Emergency stop" severity fatal
    try
      raise E_STOP
    catch E_STOP
      alarm RECOVER "Recovering" severity warning
    finally
      alarm CLEANUP "Cleanup"
    end
    enable interrupt guard
    disable interrupt guard
  end
end
`;

function declarations() {
  return parseGrl(PROGRAM).module.declarations;
}

describe("GRL alarm, raise, try/catch, interrupt, and task semantics", () => {
  it("keeps trap and task as parsed raw declarations for P1 diagnostics", () => {
    const raw = declarations().filter(
      (decl): decl is GrlRawTopLevelDeclaration => decl.kind === "RawTopLevelDeclaration"
    );

    expect(raw[0]?.declarationType).toBe("trap");
    expect(raw[0]?.tokens[0]).toMatchObject({ raw: "trap" });
    expect(raw[0]?.tokens[1]).toMatchObject({ raw: "recover_trap" });
    expect(raw[1]?.declarationType).toBe("task");
    expect(raw[1]?.tokens[0]).toMatchObject({ raw: "task" });
    expect(raw[1]?.tokens[1]).toMatchObject({ raw: "background" });
  });

  it("compiles alarm, raise, try/catch/finally, and interrupt diagnostics from procedure body", () => {
    const procedure = declarations().find(
      (decl): decl is GrlProcedureDeclaration => decl.kind === "ProcedureDeclaration"
    )!;

    expect(parseProcedureExceptionFlow(procedure)).toEqual([
      expect.objectContaining({
        kind: "ALARM",
        alarmId: "E_STOP",
        message: "Emergency stop",
        severity: "fatal"
      }),
      expect.objectContaining({
        kind: "TRY",
        body: [expect.objectContaining({ kind: "RAISE", alarmId: "E_STOP" })],
        catches: [
          expect.objectContaining({
            alarmId: "E_STOP",
            body: [
              expect.objectContaining({
                kind: "ALARM",
                alarmId: "RECOVER",
                message: "Recovering",
                severity: "warning"
              })
            ]
          })
        ],
        finally: expect.objectContaining({
          body: [expect.objectContaining({ kind: "ALARM", alarmId: "CLEANUP", message: "Cleanup" })]
        })
      }),
      expect.objectContaining({ kind: "UNSUPPORTED_RUNTIME", feature: "interrupt" }),
      expect.objectContaining({ kind: "UNSUPPORTED_RUNTIME", feature: "interrupt" })
    ]);
  });

  it("reports P1 trap/task semantics as explicit unsupported diagnostics", () => {
    const analysis = analyzeExceptionSemantics(declarations());

    expect(analysis.unsupported).toEqual([
      expect.objectContaining({ kind: "UNSUPPORTED_RUNTIME", feature: "trap" }),
      expect.objectContaining({ kind: "UNSUPPORTED_RUNTIME", feature: "task" })
    ]);
    expect(analysis.diagnostics).toEqual([
      expect.objectContaining({ severity: "warning", code: "GRL_P1_UNIMPLEMENTED" }),
      expect.objectContaining({ severity: "warning", code: "GRL_P1_UNIMPLEMENTED" })
    ]);
  });

  it("reports missing alarm ids and try blocks without handlers", () => {
    const missingAlarmId = parseGrl(`language grl 0.1
module Main
  proc main()
    alarm
  end
end
`).module.declarations.find((decl): decl is GrlProcedureDeclaration => decl.kind === "ProcedureDeclaration")!;
    const tryWithoutHandler = parseGrl(`language grl 0.1
module Main
  proc main()
    try
      raise E_STOP
    end
  end
end
`).module.declarations.find((decl): decl is GrlProcedureDeclaration => decl.kind === "ProcedureDeclaration")!;

    expect(() => parseProcedureExceptionFlow(missingAlarmId)).toThrowError(
      expect.objectContaining({ code: "GRL_ALARM_ID_MISSING" })
    );
    expect(() => parseProcedureExceptionFlow(tryWithoutHandler)).toThrowError(
      expect.objectContaining({ code: "GRL_TRY_HANDLER_MISSING" })
    );
  });
});
