import { describe, expect, it } from "vitest";
import { GrlParseError, parseGrl } from "../../src/grl/parser/index.js";

describe("GRL parser", () => {
  it("parses the minimal language/module/proc skeleton with source ranges", () => {
    const ast = parseGrl(`language grl 0.1

module Main
  proc main()
    // body comment should not affect parser
  end
end
`);

    expect(ast).toMatchObject({
      kind: "Program",
      language: {
        kind: "LanguageDeclaration",
        language: "grl",
        version: "0.1",
        range: {
          start: { line: 1, column: 1 },
          end: { line: 1, column: 17 }
        }
      },
      module: {
        kind: "ModuleDeclaration",
        name: "Main",
        declarations: [
          {
            kind: "ProcedureDeclaration",
            name: "main",
            params: []
          }
        ]
      }
    });
    expect(ast.module.range.start).toMatchObject({ line: 3, column: 1 });
    expect(ast.module.range.end).toMatchObject({ line: 7, column: 4 });
  });

  it("parses imports, data declarations, targets, and procedure body tokens", () => {
    const ast = parseGrl(`language grl 0.1
module Main
  import CommonTools
  const speed v_pick = linear(300 mm/s)
  target home = joint_target {
    joints: [0 deg, 0 deg]
  }
  proc main()
    movej home
  end
end
`);

    expect(ast.module.declarations.map((decl) => decl.kind)).toEqual([
      "ImportDeclaration",
      "DataDeclaration",
      "TargetDeclaration",
      "ProcedureDeclaration"
    ]);
    expect(ast.module.declarations[0]).toMatchObject({
      kind: "ImportDeclaration",
      moduleName: "CommonTools"
    });
    expect(ast.module.declarations[1]).toMatchObject({
      kind: "DataDeclaration",
      storage: "const",
      typeName: "speed",
      name: "v_pick",
      initializer: {
        kind: "CallExpression",
        callee: "linear"
      }
    });
    expect(ast.module.declarations[2]).toMatchObject({
      kind: "TargetDeclaration",
      name: "home",
      target: {
        kind: "ObjectExpression",
        typeName: "joint_target"
      }
    });
    expect(ast.module.declarations[3]).toMatchObject({
      kind: "ProcedureDeclaration",
      bodyTokens: [
        {
          kind: "keyword",
          raw: "movej"
        },
        {
          kind: "identifier",
          raw: "home"
        }
      ]
    });
  });

  it("reports stable line and column on invalid syntax", () => {
    expect(() => parseGrl("language grl\nmodule Main\nend\n")).toThrow(GrlParseError);
    expect(() => parseGrl("language grl\nmodule Main\nend\n")).toThrow("Expected GRL language version at 2:1");
  });
});
