import { describe, it, expect } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  EmptyAmendmentChangesError,
  PoNotFoundError,
  PoNotOrderedError,
  LineNotFoundError,
  ModifyQuantityBelowReceivedError,
  ModifyQuantityBelowBilledError,
  RemoveLineHasReceiptsError,
  RemoveLineHasBillsError,
  AddInvalidQuantityError,
  ItemNotFoundError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk, mockItemQueries } from "../testing/commandTestUtils";
import {
  baseItem,
  baseUnit,
  orderedPurchaseOrderWithRevision,
  poLineForAmendment,
} from "../testing/fixtures";
import { run } from "./amendOrderedPurchaseOrder";

const po = orderedPurchaseOrderWithRevision;
const poLine = poLineForAmendment;

describe("amendOrderedPurchaseOrder", () => {
  it("amends a PO with a MODIFY action (line ID unchanged, values updated)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED) — none
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED) — none
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.update
      .mockReturnValueOnce(undefined) // PurchaseOrderLine update
      .mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update (returningAll)

    const result = await run(
      db,
      {
        id: po.id,
        reason: "Price change",
        changes: [{ action: "MODIFY", lineId: poLine.id, quantity: "8", unitPrice: "90" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectOk(result);
  });

  it("amends a PO with a REMOVE action (line physically deleted)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED) — none
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED) — none
      .mockReturnValueOnce(undefined) // Draft GoodsReceiptLine check — none
      .mockReturnValueOnce(undefined) // Draft/blocked PurchaseBillLine check — none
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED) — empty (no lines left)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED) — empty
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.delete.mockReturnValueOnce(undefined); // PurchaseOrderLine delete
    spies.update.mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update

    const result = await run(
      db,
      {
        id: po.id,
        reason: "Line no longer needed",
        changes: [{ action: "REMOVE", lineId: poLine.id }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectOk(result);
  });

  it("amends a PO with an ADD action (new line created, no line receivingSiteId — header is used)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const newLine = {
      id: "new-po-line-1",
      purchaseOrderId: po.id,
      itemId: baseItem.id,
      itemSnapshotName: baseItem.name,
      itemSnapshotSku: baseItem.sku,
      quantity: "5",
      unitPrice: "50",
      unitId: baseUnit.id,
      receivingSiteId: null,
      requiresPhysicalReceipt: true,
      createdAt: new Date(),
      updatedAt: null,
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      // step 3 skipped (no MODIFY/REMOVE)
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert
      .mockReturnValueOnce([newLine]) // PurchaseOrderLine batch insert (returningAll)
      .mockReturnValueOnce(undefined); // PurchaseOrderLineRevision batch insert
    spies.update.mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update

    const result = await run(
      db,
      {
        id: po.id,
        changes: [
          {
            action: "ADD",
            itemId: baseItem.id,
            quantity: "5",
            unitPrice: "50",
            unitId: baseUnit.id,
          },
        ],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectOk(result);
  });

  it("creates PurchaseOrderLineRevision snapshot for MODIFY with pre-change values", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED)
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED)
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.update
      .mockReturnValueOnce(undefined) // PurchaseOrderLine update
      .mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update

    await run(
      db,
      {
        id: po.id,
        reason: "Adjust quantity",
        changes: [{ action: "MODIFY", lineId: poLine.id, quantity: "8" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );

    expect(spies.values).toHaveBeenCalledWith([
      expect.objectContaining({
        purchaseOrderLineId: poLine.id,
        purchaseOrderId: po.id,
        action: "MODIFIED",
        revisionNumber: 2,
        quantity: poLine.quantity,
        unitPrice: poLine.unitPrice,
        itemId: poLine.itemId,
      }),
    ]);
  });

  it("creates PurchaseOrderLineRevision snapshot for REMOVE with pre-deletion values", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED)
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED)
      .mockReturnValueOnce(undefined) // Draft GoodsReceiptLine check — none
      .mockReturnValueOnce(undefined) // Draft/blocked PurchaseBillLine check — none
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.delete.mockReturnValueOnce(undefined); // PurchaseOrderLine delete
    spies.update.mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update

    await run(
      db,
      {
        id: po.id,
        reason: "Remove line",
        changes: [{ action: "REMOVE", lineId: poLine.id }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );

    expect(spies.values).toHaveBeenCalledWith([
      expect.objectContaining({
        purchaseOrderLineId: poLine.id,
        purchaseOrderId: po.id,
        action: "REMOVED",
        revisionNumber: 2,
        quantity: poLine.quantity,
        unitPrice: poLine.unitPrice,
        itemId: poLine.itemId,
      }),
    ]);
  });

  it("creates PurchaseOrderLineRevision snapshot for ADD with new values", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const newLine = {
      id: "new-po-line-1",
      purchaseOrderId: po.id,
      itemId: baseItem.id,
      itemSnapshotName: baseItem.name,
      itemSnapshotSku: baseItem.sku,
      quantity: "5",
      unitPrice: "50",
      unitId: baseUnit.id,
      receivingSiteId: null,
      requiresPhysicalReceipt: true,
      createdAt: new Date(),
      updatedAt: null,
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      // step 3 skipped (no MODIFY/REMOVE)
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert
      .mockReturnValueOnce([newLine]) // PurchaseOrderLine batch insert (returningAll)
      .mockReturnValueOnce(undefined); // PurchaseOrderLineRevision batch insert
    spies.update.mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update

    await run(
      db,
      {
        id: po.id,
        reason: "Add new line",
        changes: [
          {
            action: "ADD",
            itemId: baseItem.id,
            quantity: "5",
            unitPrice: "50",
            unitId: baseUnit.id,
          },
        ],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );

    expect(spies.values).toHaveBeenCalledWith([
      expect.objectContaining({
        purchaseOrderLineId: newLine.id,
        purchaseOrderId: po.id,
        action: "ADDED",
        revisionNumber: 2,
        quantity: newLine.quantity,
        unitPrice: newLine.unitPrice,
        itemId: newLine.itemId,
      }),
    ]);
  });

  it("increments revisionNumber after amendment", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED)
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED)
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.update
      .mockReturnValueOnce(undefined) // PurchaseOrderLine update
      .mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, quantity: "8" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    const value = expectOk(result);
    expect(value.purchaseOrder.revisionNumber).toBe(2);
  });

  it("recalculates receiptStatus after amendment", async () => {
    const { db, spies } = createMockDb<Transaction>();
    // Simulate a line with partial receipt
    const grLine = {
      purchaseOrderLineId: poLine.id,
      quantity: "4",
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([grLine]) // GoodsReceiptLine joined GoodsReceipt (POSTED)
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED)
      .mockReturnValueOnce([grLine]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.update
      .mockReturnValueOnce(undefined) // PurchaseOrderLine update
      .mockReturnValueOnce({ ...po, revisionNumber: 2, receiptStatus: "PARTIALLY_RECEIVED" });

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, unitPrice: "90" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    const value = expectOk(result);
    expect(value.purchaseOrder.receiptStatus).toBe("PARTIALLY_RECEIVED");
  });

  it("recalculates billingStatus after amendment", async () => {
    const { db, spies } = createMockDb<Transaction>();
    // Simulate a line with partial billing
    const billLine = {
      purchaseOrderLineId: poLine.id,
      quantity: "4",
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED)
      .mockReturnValueOnce([billLine]) // PurchaseBillLine joined PurchaseBill (MATCHED)
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([billLine]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.update
      .mockReturnValueOnce(undefined) // PurchaseOrderLine update
      .mockReturnValueOnce({ ...po, revisionNumber: 2, billingStatus: "PARTIALLY_BILLED" });

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, unitPrice: "90" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    const value = expectOk(result);
    expect(value.purchaseOrder.billingStatus).toBe("PARTIALLY_BILLED");
  });

  it("supports price-only amendment (quantity not provided)", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED)
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED)
      .mockReturnValueOnce([]) // Recalc: GoodsReceiptLine (POSTED)
      .mockReturnValueOnce([]); // Recalc: PurchaseBillLine (MATCHED)
    spies.insert.mockReturnValueOnce(undefined); // PurchaseOrderLineRevision insert
    spies.update
      .mockReturnValueOnce(undefined) // PurchaseOrderLine update
      .mockReturnValueOnce({ ...po, revisionNumber: 2 }); // PurchaseOrder update

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, unitPrice: "120" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectOk(result);
    // quantity should be preserved — set spy called with original quantity
    expect(spies.set).toHaveBeenCalledWith(expect.objectContaining({ quantity: poLine.quantity }));
  });

  it("returns error when changes array is empty", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(po); // PurchaseOrder (forUpdate)

    const result = await run(
      db,
      { id: po.id, changes: [] },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, EmptyAmendmentChangesError);
  });

  it("returns error when PO does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined); // PurchaseOrder not found

    const result = await run(
      db,
      { id: "missing-po", changes: [] },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, PoNotFoundError);
  });

  it("returns error when PO is not ORDERED", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({ ...po, orderStatus: "DRAFT" }); // PO is DRAFT

    const result = await run(
      db,
      { id: po.id, changes: [] },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, PoNotOrderedError);
  });

  it("returns error when MODIFY references a non-existent line", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine — does not contain "missing-line"
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED)
      .mockReturnValueOnce([]); // PurchaseBillLine joined PurchaseBill (MATCHED)

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: "missing-line", quantity: "5" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, LineNotFoundError);
  });

  it("returns error when MODIFY reduces quantity below received", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const grLine = {
      purchaseOrderLineId: poLine.id,
      quantity: "8",
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([grLine]) // GoodsReceiptLine joined GoodsReceipt (POSTED) — received 8
      .mockReturnValueOnce([]); // PurchaseBillLine joined PurchaseBill (MATCHED)

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, quantity: "5" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, ModifyQuantityBelowReceivedError);
  });

  it("returns error when MODIFY reduces quantity below billed", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const billLine = {
      purchaseOrderLineId: poLine.id,
      quantity: "8",
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED) — none
      .mockReturnValueOnce([billLine]); // PurchaseBillLine joined PurchaseBill (MATCHED) — billed 8

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, quantity: "5" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, ModifyQuantityBelowBilledError);
  });

  it("returns error when MODIFY reduces quantity below posted + draft received", async () => {
    const { db, spies } = createMockDb<Transaction>();
    // posted=3, draft=5 → total non-cancelled received=8, new quantity=6 → 6 < 8 → error
    const grLines = [
      { purchaseOrderLineId: poLine.id, quantity: "3" }, // POSTED
      { purchaseOrderLineId: poLine.id, quantity: "5" }, // DRAFT (both returned by != CANCELLED)
    ];
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce(grLines) // GoodsReceiptLine joined GoodsReceipt (!= CANCELLED)
      .mockReturnValueOnce([]); // PurchaseBillLine joined PurchaseBill (!= CANCELLED)

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, quantity: "6" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, ModifyQuantityBelowReceivedError);
  });

  it("returns error when MODIFY reduces quantity below matched + draft/blocked billed", async () => {
    const { db, spies } = createMockDb<Transaction>();
    // matched=2, draft=5 → total non-cancelled billed=7, new quantity=6 → 6 < 7 → error
    const billLines = [
      { purchaseOrderLineId: poLine.id, quantity: "2" }, // MATCHED
      { purchaseOrderLineId: poLine.id, quantity: "5" }, // DRAFT (both returned by != CANCELLED)
    ];
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (!= CANCELLED)
      .mockReturnValueOnce(billLines); // PurchaseBillLine joined PurchaseBill (!= CANCELLED)

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "MODIFY", lineId: poLine.id, quantity: "6" }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, ModifyQuantityBelowBilledError);
  });

  it("returns error when REMOVE targets a line with posted receipts", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const grLine = {
      purchaseOrderLineId: poLine.id,
      quantity: "4",
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([grLine]) // GoodsReceiptLine joined GoodsReceipt (POSTED) — received 4
      .mockReturnValueOnce([]); // PurchaseBillLine joined PurchaseBill (MATCHED)

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "REMOVE", lineId: poLine.id }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, RemoveLineHasReceiptsError);
  });

  it("returns error when REMOVE targets a line with matched bills", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const billLine = {
      purchaseOrderLineId: poLine.id,
      quantity: "4",
    };
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED) — none
      .mockReturnValueOnce([billLine]); // PurchaseBillLine joined PurchaseBill (MATCHED) — billed 4

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "REMOVE", lineId: poLine.id }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, RemoveLineHasBillsError);
  });

  it("returns error when REMOVE targets a line referenced by draft downstream documents", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]) // PurchaseOrderLine
      .mockReturnValueOnce([]) // GoodsReceiptLine joined GoodsReceipt (POSTED) — none
      .mockReturnValueOnce([]) // PurchaseBillLine joined PurchaseBill (MATCHED) — none
      .mockReturnValueOnce({ id: "draft-gr-line-1" }); // Draft GoodsReceiptLine check — found

    const result = await run(
      db,
      {
        id: po.id,
        changes: [{ action: "REMOVE", lineId: poLine.id }],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect((result.error as Error).name).toBe("RemoveLineHasDownstreamReferencesError");
    }
  });

  it("returns error when ADD has zero or negative quantity", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]); // PurchaseOrderLine

    const result = await run(
      db,
      {
        id: po.id,
        changes: [
          {
            action: "ADD",
            itemId: baseItem.id,
            quantity: "0",
            unitPrice: "50",
            unitId: baseUnit.id,
          },
        ],
      },
      commandCtx,
      mockItemQueries({ item: baseItem }),
    );
    expectErr(result, AddInvalidQuantityError);
  });

  it("returns error when ADD references a non-existent item", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(po) // PurchaseOrder (forUpdate)
      .mockReturnValueOnce([poLine]); // PurchaseOrderLine

    const result = await run(
      db,
      {
        id: po.id,
        changes: [
          {
            action: "ADD",
            itemId: "non-existent-item",
            quantity: "5",
            unitPrice: "50",
            unitId: baseUnit.id,
          },
        ],
      },
      commandCtx,
      mockItemQueries({ item: null }),
    );
    expectErr(result, ItemNotFoundError);
  });
});
