import { describe, expect, it } from "vitest";
import {
  ReorderQueueHandler,
  i1Ids,
  i1Owner,
  i1Project,
  i1Tasks,
  type ApplicationTransaction,
  type UnitOfWork,
} from "@awp/application";
import {
  authorityContext,
  capability,
  unsafeOpaqueId,
  type AuditRecord,
  type BusinessEvent,
  type CorrelationId,
  type OperationId,
} from "@awp/contracts";

function harness() {
  const events: BusinessEvent[] = [];
  const audit: AuditRecord[] = [];
  const outbox: unknown[] = [];
  const tx = {
    projects: { getById: async () => i1Project },
    tasks: { listByProject: async () => i1Tasks },
    events: { append: async (value: BusinessEvent) => void events.push(value) },
    audit: { append: async (value: AuditRecord) => void audit.push(value) },
    outbox: { append: async (value: unknown) => void outbox.push(value) },
  } as unknown as ApplicationTransaction;
  const uow: UnitOfWork = { transaction: async (work) => work(tx) };
  let sequence = 0;
  const handler = new ReorderQueueHandler(
    uow,
    { next: () => unsafeOpaqueId(`generated-${++sequence}`) },
    { now: () => new Date("2026-08-20T00:00:00Z") },
  );
  return { handler, events, audit, outbox };
}

describe("queue command contract", () => {
  const context = {
    operationId: unsafeOpaqueId<OperationId>("op"),
    correlationId: unsafeOpaqueId<CorrelationId>("correlation"),
    idempotencyKey: "queue-move-1",
    authority: authorityContext(i1Owner, [capability("queue.reorder")], i1Ids.project),
  };

  it("records event, audit, and outbox only after a legal move", async () => {
    const h = harness();
    const order = await h.handler.execute({
      projectId: i1Ids.project,
      taskId: i1Ids.taskUi,
      desiredPosition: 1,
      currentOrder: [i1Ids.taskFoundation, i1Ids.taskUi],
      context,
    });
    expect(order).toEqual([i1Ids.taskFoundation, i1Ids.taskUi]);
    expect(h.events).toHaveLength(1);
    expect(h.audit).toHaveLength(1);
    expect(h.outbox).toHaveLength(1);
  });

  it("rejects an illegal dependency reorder before emitting transactional records", async () => {
    const h = harness();
    await expect(
      h.handler.execute({
        projectId: i1Ids.project,
        taskId: i1Ids.taskUi,
        desiredPosition: 0,
        currentOrder: [i1Ids.taskFoundation, i1Ids.taskUi],
        context,
      }),
    ).rejects.toMatchObject({ code: "QUEUE_ORDER_CONSTRAINT" });
    expect(h.events).toHaveLength(0);
    expect(h.audit).toHaveLength(0);
    expect(h.outbox).toHaveLength(0);
  });
});
