import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  ItemNotActiveError,
  PartnerNotSupplierError,
  PurchaseOrderNotFoundError,
  PurchaseOrderNotSubmittedError,
  SupplierNotActiveError,
} from "../lib/errors.generated";
import {
  commandCtx,
  expectErr,
  expectOk,
  mockBPQueries,
  mockItemQueries,
} from "../testing/commandTestUtils";
import {
  baseItem,
  basePurchaseOrderLine,
  basePurchaseOrder,
  submittedPurchaseOrder,
} from "../testing/fixtures";
import { run } from "./approvePurchaseOrder";

describe("approvePurchaseOrder", () => {
  it("approves a valid submitted purchase order", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const line = { ...basePurchaseOrderLine, purchaseOrderId: submittedPurchaseOrder.id };
    const bpQ = mockBPQueries({
      partner: { id: submittedPurchaseOrder.supplierId, status: "ACTIVE" },
      partnerRole: { role: "SUPPLIER" },
    });
    const itemQ = mockItemQueries({ item: baseItem });
    spies.select
      .mockReturnValueOnce(submittedPurchaseOrder) // selectOne PurchaseOrder
      .mockReturnValueOnce([line]); // selectFrom PurchaseOrderLine (execute → array)
    spies.update.mockReturnValueOnce({
      ...submittedPurchaseOrder,
      orderStatus: "ORDERED",
      receiptStatus: "NOT_RECEIVED",
      billingStatus: "NOT_BILLED",
    });

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

    expect(expectOk(result).purchaseOrder.orderStatus).toBe("ORDERED");
    expect(expectOk(result).purchaseOrder.receiptStatus).toBe("NOT_RECEIVED");
    expect(expectOk(result).purchaseOrder.billingStatus).toBe("NOT_BILLED");
  });

  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,
      mockBPQueries(),
      mockItemQueries(),
    );

    expectErr(result, PurchaseOrderNotFoundError);
  });

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

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

    expectErr(result, PurchaseOrderNotSubmittedError);
  });

  it("returns error when supplier is inactive", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const bpQ = mockBPQueries({
      partner: { id: submittedPurchaseOrder.supplierId, status: "INACTIVE" },
    });
    const itemQ = mockItemQueries();
    spies.select.mockReturnValueOnce(submittedPurchaseOrder);

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

    expectErr(result, SupplierNotActiveError);
  });

  it("returns error when an item is inactive", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const line = { ...basePurchaseOrderLine, purchaseOrderId: submittedPurchaseOrder.id };
    const bpQ = mockBPQueries({
      partner: { id: submittedPurchaseOrder.supplierId, status: "ACTIVE" },
      partnerRole: { role: "SUPPLIER" },
    });
    const itemQ = mockItemQueries({ item: { ...baseItem, status: "INACTIVE" } });
    spies.select
      .mockReturnValueOnce(submittedPurchaseOrder) // selectOne PurchaseOrder
      .mockReturnValueOnce([line]); // selectFrom PurchaseOrderLine (execute → array)

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

    expectErr(result, ItemNotActiveError);
  });

  it("returns error when supplier is no longer classified as a supplier", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const bpQ = mockBPQueries({
      partner: { id: submittedPurchaseOrder.supplierId, status: "ACTIVE" },
      partnerRole: undefined,
    });
    const itemQ = mockItemQueries();
    spies.select.mockReturnValueOnce(submittedPurchaseOrder);

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

    expectErr(result, PartnerNotSupplierError);
  });
});
