import { describe, expect, it } from "vitest";
import { postProcessAllBrands } from "../../src/grl/post/index.js";
import { parseGrl } from "../../src/grl/parser/index.js";
import { compileSemanticProgram } from "../../src/grl/semantic/index.js";
import {
  applyPatch,
  pathOperationToGrl,
  sampleOlpProject,
  validateOlpProject,
  type OlpProjectModel
} from "../../src/olp/index.js";

describe("OLP project model", () => {
  it("creates a serializable station/resource/path/operation sample", () => {
    const project = sampleOlpProject();
    const restored = JSON.parse(JSON.stringify(project)) as OlpProjectModel;
    const report = validateOlpProject(restored);

    expect(restored.schemaVersion).toBe("olp/0.1");
    expect(report).toEqual({ ok: true, issues: [] });
    expect(restored.resources.robots[0]).toMatchObject({
      id: "robot_1",
      kind: "robot",
      brand: "abb"
    });
    expect(restored.externalAxes[0]).toMatchObject({
      id: "track_1",
      axisKind: "linear"
    });
    expect(restored.motionGroups[0]).toMatchObject({
      robotIds: ["robot_1"],
      externalAxisIds: ["track_1"],
      coordination: "synchronized"
    });
  });

  it("applies brand import style object patches and validates references", () => {
    const patched = applyPatch(sampleOlpProject(), [
      {
        op: "add",
        path: "/targets/-",
        value: {
          id: "inspect",
          name: "inspect",
          kind: "pose",
          robotId: "robot_1",
          pose: [650, 25, 0, 0, 0, 0],
          toolId: "tool_gripper",
          frameId: "frame_fixture"
        }
      },
      {
        op: "add",
        path: "/paths/0/points/-",
        value: {
          id: "p03",
          name: "p03",
          motion: "movel",
          targetId: "inspect",
          speedId: "v_linear",
          zoneId: "z10"
        }
      }
    ]);

    expect(validateOlpProject(patched).ok).toBe(true);
    expect(patched.paths[0]?.points.map((point) => point.name)).toEqual(["p00", "p01", "p02", "p03"]);
  });

  it("generates stable parseable GRL from Path and Operation", () => {
    const result = pathOperationToGrl(sampleOlpProject(), "pick_op", {
      moduleName: "DemoCell",
      style: "expanded"
    });
    const ast = parseGrl(result.text);
    const ir = compileSemanticProgram(ast, {
      startJoints: [0, 0, 0, 0, 0, 0],
      sampleTime: 0.004
    });
    const post = postProcessAllBrands(ir);

    expect(result.stableIds).toEqual({
      targets: ["home", "pick", "place"],
      points: ["p00", "p01", "p02"],
      path: "pick_path",
      operation: "pick_op"
    });
    expect(result.text).toContain("path pick_path");
    expect(result.text).toContain("operation pick_op");
    expect(result.program).toMatchObject({
      id: "pick_op_grl",
      language: "grl",
      entryOperationIds: ["pick_op"]
    });
    expect(ir.operations[0]).toMatchObject({
      operationId: "pick_op",
      kind: "handling",
      pathId: "pick_path"
    });
    expect(post.outputs.abb.text).toContain("MODULE DemoCell");
    expect(post.outputs.fanuc.text).toContain("/PROG MAIN");
    expect(post.outputs.kuka.text).toContain("DEF Main()");
  });

  it("reports broken schema references with stable issue codes", () => {
    const project = sampleOlpProject();
    project.operations[0]!.pathId = "missing_path";

    expect(validateOlpProject(project)).toMatchObject({
      ok: false,
      issues: [expect.objectContaining({ code: "OLP_REF_PATH_MISSING" })]
    });
  });
});
