import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  PurchaseOrderAlreadyFulfilledError,
  PurchaseOrderNotCancellableError,
  PurchaseOrderNotFoundError,
} from "../lib/errors.generated";
import { commandCtx, expectErr } from "../testing/commandTestUtils";
import {
  basePurchaseOrderLine,
  basePurchaseOrder,
  orderedPurchaseOrder,
  baseReceiptEvidence,
  submittedPurchaseOrder,
} from "../testing/fixtures";
import {
  mockInventoryCommands,
  mockInventoryQueries,
  type ReceiptEvidence,
} from "../testing/moduleMocks";
import { run as runCommand } from "./cancelPurchaseOrder";

type KMock = ReturnType<typeof createKyselyMock<Namespace["main-db"]>>;

function run(
  mock: KMock,
  input: Parameters<typeof runCommand>[1],
  ctx: Parameters<typeof runCommand>[2],
  receiptEvidence: ReceiptEvidence[] = [],
  inventoryCommands: Parameters<typeof runCommand>[3] = mockInventoryCommands(),
) {
  return mock.withTx((trx) =>
    runCommand(trx, input, ctx, inventoryCommands, mockInventoryQueries({ receiptEvidence })),
  );
}

describe("cancelPurchaseOrder", () => {
  it("cancels a draft purchase order", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const cancelled = { ...basePurchaseOrder, orderStatus: "CANCELLED" };
    mock.enqueueResults(
      [basePurchaseOrder], // selectOne PurchaseOrder
      [], // selectFrom PurchaseOrderLine (execute → array)
      [cancelled],
    );

    const result = await run(mock, { id: basePurchaseOrder.id }, commandCtx);

    expect(result.ok).toBe(true);
    if (!result.ok) throw result.error;
    expect(result.value.purchaseOrder.orderStatus).toBe("CANCELLED");
    expect(mock.updates[0].updateValues()).toEqual(
      expect.objectContaining({ orderStatus: "CANCELLED" }),
    );
  });

  it("cancels a submitted purchase order", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const cancelled = { ...submittedPurchaseOrder, orderStatus: "CANCELLED" };
    mock.enqueueResults(
      [submittedPurchaseOrder], // selectOne PurchaseOrder
      [], // selectFrom PurchaseOrderLine (execute → array)
      [cancelled],
    );

    const result = await run(mock, { id: submittedPurchaseOrder.id }, commandCtx);

    expect(result.ok).toBe(true);
    if (!result.ok) throw result.error;
    expect(result.value.purchaseOrder.orderStatus).toBe("CANCELLED");
  });

  it("cancels an ordered purchase order with no receipts or billed quantity", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const poLine = { ...basePurchaseOrderLine, purchaseOrderId: orderedPurchaseOrder.id };
    const cancelled = { ...orderedPurchaseOrder, orderStatus: "CANCELLED" };
    mock.enqueueResults(
      [orderedPurchaseOrder], // selectOne PurchaseOrder
      [poLine], // selectFrom PurchaseOrderLine (execute → array)
      [cancelled],
    );

    const result = await run(mock, { id: orderedPurchaseOrder.id }, commandCtx);

    expect(result.ok).toBe(true);
    if (!result.ok) throw result.error;
    expect(result.value.purchaseOrder.orderStatus).toBe("CANCELLED");
  });

  it("returns error when purchase order does not exist", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await run(mock, { id: "missing-order" }, commandCtx);

    expectErr(result, PurchaseOrderNotFoundError);
  });

  it("returns error when purchase order status does not allow cancellation", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([{ ...orderedPurchaseOrder, orderStatus: "CLOSED" }]);

    const result = await run(mock, { id: orderedPurchaseOrder.id }, commandCtx);

    expectErr(result, PurchaseOrderNotCancellableError);
  });

  it("returns error when a posted receipt already exists", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const poLine = { ...basePurchaseOrderLine, purchaseOrderId: orderedPurchaseOrder.id };
    mock.enqueueResults(
      [orderedPurchaseOrder], // selectOne PurchaseOrder
      [poLine], // selectFrom PurchaseOrderLine (execute → array)
    );

    const result = await run(mock, { id: orderedPurchaseOrder.id }, commandCtx, [
      baseReceiptEvidence,
    ]);

    expectErr(result, PurchaseOrderAlreadyFulfilledError);
  });

  it("returns error when billed quantity already exists", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const poLine = {
      ...basePurchaseOrderLine,
      purchaseOrderId: orderedPurchaseOrder.id,
      billedQuantity: "4",
    };
    mock.enqueueResults(
      [orderedPurchaseOrder], // selectOne PurchaseOrder
      [poLine], // selectFrom PurchaseOrderLine (execute → array)
    );

    const result = await run(mock, { id: orderedPurchaseOrder.id }, commandCtx);

    expectErr(result, PurchaseOrderAlreadyFulfilledError);
  });
});
