import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  DuplicateExternalSupplierOrderReferenceError,
  EmptyPurchaseOrderLinesError,
  PartnerNotSupplierError,
  PurchaseOrderNotDraftError,
  PurchaseOrderNotFoundError,
  ReceivingSiteRequiredError,
} from "../lib/errors.generated";
import { basePurchaseOrder } from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./submitPurchaseOrder";

describe("submitPurchaseOrder", () => {
  it("submits a valid draft purchase order", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(basePurchaseOrder)
      .mockReturnValueOnce({ role: "SUPPLIER" })
      .mockReturnValueOnce(undefined);
    spies.update.mockReturnValueOnce({ ...basePurchaseOrder, status: "SUBMITTED" });

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

    expect(expectOk(result).purchaseOrder.status).toBe("SUBMITTED");
  });

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

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

    expectErr(result, PurchaseOrderNotFoundError);
  });

  it("returns error when purchase order is not in draft", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({ ...basePurchaseOrder, status: "SUBMITTED" });

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

    expectErr(result, PurchaseOrderNotDraftError);
  });

  it("returns error when purchase order has no lines", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({ ...basePurchaseOrder, lines: JSON.stringify([]) });

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

    expectErr(result, EmptyPurchaseOrderLinesError);
  });

  it("returns error when a physical line has no receiving site", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({
      ...basePurchaseOrder,
      lines: JSON.stringify([
        {
          id: "po-line-1",
          itemId: "item-1",
          quantity: 10,
          unitPrice: 100,
          requiresPhysicalReceipt: true,
          receivingSiteId: null,
        },
      ]),
    });

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

    expectErr(result, ReceivingSiteRequiredError);
  });

  it("returns error when supplier is no longer classified as a supplier", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(basePurchaseOrder).mockReturnValueOnce(undefined);

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

    expectErr(result, PartnerNotSupplierError);
  });

  it("returns error when external supplier order reference is duplicated", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(basePurchaseOrder)
      .mockReturnValueOnce({ role: "SUPPLIER" })
      .mockReturnValueOnce({ id: "duplicate-order" });

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

    expectErr(result, DuplicateExternalSupplierOrderReferenceError);
  });
});
