import { describe, expect, it } from "vitest";
import { unsafeOpaqueId, type PlanRevisionId, type ProjectId, type TaskId } from "@awp/contracts";
import {
  buildQueue,
  isDispatchEligible,
  legalPlacementRange,
  moveTaskToPosition,
  QueueOrderConstraintError,
  type Task,
} from "@awp/domain";
const p = unsafeOpaqueId<ProjectId>("p"),
  r = unsafeOpaqueId<PlanRevisionId>("r");
const id = (x: string) => unsafeOpaqueId<TaskId>(x);
const task = (x: string, deps: string[] = [], status: Task["status"] = "planned"): Task => ({
  id: id(x),
  projectId: p,
  planRevisionId: r,
  title: x,
  status,
  dependencyIds: deps.map(id),
  revision: 1,
});
describe("dependency-safe queue", () => {
  const tasks = [task("a", [], "completed"), task("b", ["a"]), task("c", ["b"])];
  it("projects readiness and dispatch eligibility", () => {
    const q = buildQueue(tasks, [id("a"), id("b"), id("c")]);
    expect(q.map((x) => x.readinessState)).toEqual(["DONE", "READY", "BLOCKED_DEPENDENCY"]);
    expect(isDispatchEligible(tasks[1]!, tasks)).toBe(true);
    expect(isDispatchEligible(tasks[2]!, tasks)).toBe(false);
  });
  it("computes legal range from prerequisites and dependents", () => {
    expect(legalPlacementRange(tasks[1]!, [id("a"), id("b"), id("c")], tasks)).toMatchObject({
      earliestLegalPosition: 1,
      latestLegalPosition: 1,
      blockingDependencies: [id("a")],
      blockingDependents: [id("c")],
    });
  });
  it("rejects an illegal direct reorder", () => {
    expect(() => moveTaskToPosition([id("a"), id("b"), id("c")], id("c"), 0, tasks)).toThrow(
      QueueOrderConstraintError,
    );
  });
});
