import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { basePurchaseChangeOrder, baseChangeOrderLine } from "../testing/fixtures";
import { expectOk } from "../testing/commandTestUtils";
import { run } from "./getPurchaseChangeOrder";

describe("getPurchaseChangeOrder", () => {
  it("returns change order when found by id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseChangeOrder);
    spies.select.mockReturnValueOnce([baseChangeOrderLine]);

    const result = await run(db, { id: basePurchaseChangeOrder.id });
    const { purchaseChangeOrder } = expectOk<{ purchaseChangeOrder: unknown }>(result as never);

    expect(purchaseChangeOrder).not.toBeNull();
    expect(purchaseChangeOrder).toMatchObject({ id: basePurchaseChangeOrder.id });
  });

  it("returns open change order when found by purchaseOrderId", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseChangeOrder);
    spies.select.mockReturnValueOnce([baseChangeOrderLine]);

    const result = await run(db, { purchaseOrderId: basePurchaseChangeOrder.purchaseOrderId });
    const { purchaseChangeOrder } = expectOk<{ purchaseChangeOrder: unknown }>(result as never);

    expect(purchaseChangeOrder).not.toBeNull();
    expect(purchaseChangeOrder).toMatchObject({ id: basePurchaseChangeOrder.id });
  });

  it("returns change order lines with their actions and quantities", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseChangeOrder);
    spies.select.mockReturnValueOnce([baseChangeOrderLine]);

    const result = await run(db, { id: basePurchaseChangeOrder.id });
    const { purchaseChangeOrder } = expectOk<{
      purchaseChangeOrder: { lines: unknown[] };
    }>(result as never);

    expect(purchaseChangeOrder.lines).toHaveLength(1);
    expect(purchaseChangeOrder.lines[0]).toMatchObject({
      id: baseChangeOrderLine.id,
      action: baseChangeOrderLine.action,
      quantity: baseChangeOrderLine.quantity,
      originalPurchaseOrderLineId: baseChangeOrderLine.originalPurchaseOrderLineId,
    });
  });

  it("returns change orders in any status including APPLIED and REJECTED", async () => {
    const appliedChangeOrder = {
      ...basePurchaseChangeOrder,
      id: "change-order-applied",
      changeOrderStatus: "APPLIED" as const,
    };
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(appliedChangeOrder);
    spies.select.mockReturnValueOnce([]);

    const result = await run(db, { id: appliedChangeOrder.id });
    const { purchaseChangeOrder } = expectOk<{ purchaseChangeOrder: unknown }>(result as never);

    expect(purchaseChangeOrder).not.toBeNull();
    expect(purchaseChangeOrder).toMatchObject({
      id: appliedChangeOrder.id,
      changeOrderStatus: "APPLIED",
    });
  });

  it("returns null when change order is not found", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { id: "non-existent-id" });
    const { purchaseChangeOrder } = expectOk<{ purchaseChangeOrder: null }>(result as never);

    expect(purchaseChangeOrder).toBeNull();
  });

  it("returns null when no open change order exists for the given purchaseOrderId", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { purchaseOrderId: "po-with-no-open-co" });
    const { purchaseChangeOrder } = expectOk<{ purchaseChangeOrder: null }>(result as never);

    expect(purchaseChangeOrder).toBeNull();
  });
});
