import { describe, expect, it } from "vitest";
import {
  applyCalibrationRecords,
  commercialExtensionBoundaries,
  compareControllerLowSpeedRun,
  createResourceLibraryTemplate,
  detectBasicCollisions,
  generatePathFromGeometry,
  instantiateProcessTemplate,
  locateCollisionTime,
  sampleOlpProject,
  validateOlpProject
} from "../../src/olp/index.js";

describe("OLP geometry, calibration, and multi-robot extensions", () => {
  it("generates path targets from points, edges, and curves", () => {
    const pointPath = generatePathFromGeometry(
      { kind: "points", points: [[500, 0, 0], [600, 0, 0]] },
      { pathId: "point_path", speedId: "v_linear", zoneId: "z10" }
    );
    const edgePath = generatePathFromGeometry(
      { kind: "edge", start: [0, 0, 0], end: [100, 0, 0], samples: 3 },
      { pathId: "edge_path" }
    );
    const curvePath = generatePathFromGeometry(
      { kind: "curve", controlPoints: [[0, 0, 0], [50, 50, 0], [100, 0, 0]], samples: 3 },
      { pathId: "curve_path", targetPrefix: "curve" }
    );

    expect(pointPath.targets.map((target) => target.pose)).toEqual([
      [500, 0, 0, 0, 0, 0],
      [600, 0, 0, 0, 0, 0]
    ]);
    expect(edgePath.targets.map((target) => target.pose?.[0])).toEqual([0, 50, 100]);
    expect(curvePath.targets.map((target) => target.name)).toEqual(["curve_00", "curve_01", "curve_02"]);
    expect(curvePath.path.points.map((point) => point.motion)).toEqual(["movej", "movel", "movel"]);
  });

  it("detects primitive collisions and locates the first time point", () => {
    const project = sampleOlpProject();
    const report = detectBasicCollisions([
      { time: 0, pointId: "p0", position: [100, 0, 0] },
      { time: 1.25, pointId: "p1", position: [550, 0, -40] }
    ], project.resources.collisionObjects);

    expect(report.ok).toBe(false);
    expect(report.hits).toEqual([
      expect.objectContaining({
        objectId: "fixture_box",
        pointId: "p1",
        time: 1.25
      })
    ]);
    expect(locateCollisionTime(report)).toBe(1.25);
  });

  it("saves and applies TCP/frame/base/external-axis calibration records", () => {
    const project = sampleOlpProject();
    const calibrated = applyCalibrationRecords(project, [
      {
        id: "tcp_cal_2",
        name: "tcp_cal_2",
        kind: "tcp",
        targetResourceId: "tool_gripper",
        poseDelta: [1, 2, 3, 0, 0, 0]
      },
      {
        id: "fixture_cal_1",
        name: "fixture_cal_1",
        kind: "frame",
        targetResourceId: "frame_fixture",
        poseDelta: [10, 0, 0, 0, 0, 0]
      },
      {
        id: "track_cal_1",
        name: "track_cal_1",
        kind: "external_axis",
        targetResourceId: "track_1",
        axisOffset: 2.5
      }
    ]);

    expect(calibrated.resources.tools.find((tool) => tool.id === "tool_gripper")?.tcp).toEqual([1, 2, 103, 0, 0, 0]);
    expect(calibrated.resources.frames.find((frame) => frame.id === "frame_fixture")?.pose).toEqual([810, 0, 0, 0, 0, 0]);
    expect(calibrated.externalAxes[0]?.metadata).toEqual({ calibrationOffset: 2.5 });
    expect(validateOlpProject(calibrated).ok).toBe(true);
  });

  it("creates resource library and process template delivery boundaries", () => {
    const project = sampleOlpProject();
    const library = createResourceLibraryTemplate(project);
    const template = instantiateProcessTemplate(project.processTemplates[0]!, "pick_op");
    const boundaries = commercialExtensionBoundaries();

    expect(library).toMatchObject({
      robots: [{ id: "robot_1", brand: "abb", model: "IRB120" }],
      collisionObjects: [{ id: "fixture_box" }]
    });
    expect(template).toEqual({
      operationId: "pick_op",
      operationKind: "handling",
      defaults: { speedId: "v_linear", zoneId: "z10" },
      deliveryTags: ["source", "post", "report"]
    });
    expect(boundaries).toEqual(expect.arrayContaining([
      expect.objectContaining({ feature: "cad_kernel", mvp: false }),
      expect.objectContaining({ feature: "delivery_package", mvp: true })
    ]));
  });

  it("reports multi-robot/external-axis low-speed controller verification boundaries", () => {
    const project = sampleOlpProject();
    const report = compareControllerLowSpeedRun([
      {
        pointId: "p01",
        offline: [500, 0, 0, 0, 0, 0],
        measured: [501, 0, 0, 0.1, 0, 0],
        speedPercent: 10
      },
      {
        pointId: "p02",
        offline: [600, 0, 0, 0, 0, 0],
        measured: [604, 0, 0, 0.5, 0, 0],
        speedPercent: 10
      }
    ], {
      positionMm: 5,
      orientationDeg: 1
    });

    expect(project.motionGroups[0]).toMatchObject({
      robotIds: ["robot_1"],
      externalAxisIds: ["track_1"]
    });
    expect(report).toMatchObject({
      status: "pass",
      speedPercent: 10,
      maxPositionErrorMm: 4,
      maxOrientationErrorDeg: 0.5
    });
    expect(report.boundaries).toEqual(expect.arrayContaining([
      expect.stringContaining("does not open a live controller communication session"),
      expect.stringContaining("does not include a full CAD kernel")
    ]));
  });
});
