import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { expectOk } from "../testing/commandTestUtils";
import {
  basePurchaseBillLine,
  basePurchaseOrder,
  basePurchaseOrderLine,
  matchedPurchaseBill,
} from "../testing/fixtures";
import { run } from "./getPurchaseOrder";

/** Mock purchase-owned fulfillment queries (PurchaseBillLine, PurchaseBill) with empty arrays */
function mockEmptyFulfillment(spies: { select: { mockReturnValueOnce: (v: unknown) => void } }) {
  spies.select.mockReturnValueOnce([]); // PurchaseBillLine
  spies.select.mockReturnValueOnce([]); // PurchaseBill (MATCHED)
}

describe("getPurchaseOrder", () => {
  it("returns purchase order when found by id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseOrder);
    spies.select.mockReturnValueOnce([basePurchaseOrderLine]);
    mockEmptyFulfillment(spies);

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

    expect(purchaseOrder).not.toBeNull();
    expect(purchaseOrder).toMatchObject({ id: basePurchaseOrder.id });
  });

  it("returns purchase order when found by company and external supplier order reference", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseOrder);
    spies.select.mockReturnValueOnce(basePurchaseOrder);
    spies.select.mockReturnValueOnce([basePurchaseOrderLine]);
    mockEmptyFulfillment(spies);

    const result = await run(db, {
      companyId: basePurchaseOrder.companyId,
      externalSupplierOrderReference: basePurchaseOrder.externalSupplierOrderReference,
    });
    const { purchaseOrder } = expectOk<{ purchaseOrder: unknown }>(result as never);

    expect(purchaseOrder).not.toBeNull();
    expect(purchaseOrder).toMatchObject({ id: basePurchaseOrder.id });
  });

  it("returns lines with the order", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseOrder);
    spies.select.mockReturnValueOnce([basePurchaseOrderLine]);
    mockEmptyFulfillment(spies);

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

    expect(purchaseOrder.lines).toHaveLength(1);
    expect(purchaseOrder.lines[0]).toMatchObject({ id: basePurchaseOrderLine.id });
  });

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

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

    expect(purchaseOrder).toBeNull();
  });

  it("returns billed quantities per line without receipt-derived fields", async () => {
    const matchedBillLine = {
      ...basePurchaseBillLine,
      purchaseBillId: matchedPurchaseBill.id,
      purchaseOrderLineId: basePurchaseOrderLine.id,
      quantity: "2",
    };
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseOrder);
    spies.select.mockReturnValueOnce([basePurchaseOrderLine]);
    spies.select.mockReturnValueOnce([matchedBillLine]); // PurchaseBillLine
    spies.select.mockReturnValueOnce([matchedPurchaseBill]); // PurchaseBill (MATCHED)

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

    expect(purchaseOrder.lines[0]).toMatchObject({
      billedQuantity: "2",
      remainingQuantityToBill: "8", // 10 - 2
    });
    expect(purchaseOrder.lines[0]).not.toHaveProperty("receivedQuantity");
    expect(purchaseOrder.lines[0]).not.toHaveProperty("remainingQuantityToReceive");
    expect(purchaseOrder.lines[0]).not.toHaveProperty("remainingReceivedQuantityToBill");
  });

  it("does not expose remaining received quantity from the purchase query", async () => {
    const matchedBillLine = {
      ...basePurchaseBillLine,
      purchaseBillId: matchedPurchaseBill.id,
      purchaseOrderLineId: basePurchaseOrderLine.id,
      quantity: "5",
    };
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(basePurchaseOrder);
    spies.select.mockReturnValueOnce([basePurchaseOrderLine]);
    spies.select.mockReturnValueOnce([matchedBillLine]);
    spies.select.mockReturnValueOnce([matchedPurchaseBill]);

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

    expect(purchaseOrder.lines[0]).toMatchObject({
      remainingQuantityToBill: "5",
    });
    expect(purchaseOrder.lines[0]).not.toHaveProperty("remainingReceivedQuantityToBill");
  });
});
