import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { baseActiveWarehouse, baseStockLevel } from "../testing/fixtures";
import { expectOk, expectErr } from "../testing/commandTestUtils";
import { WarehouseNotFoundError } from "../lib/errors.generated";
import { run } from "./getWarehouseStockSummary";

describe("getWarehouseStockSummary", () => {
  it("returns aggregated quantities per item", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseActiveWarehouse);
    spies.select.mockReturnValueOnce([{ id: "location-1" }]);
    spies.select.mockReturnValueOnce([baseStockLevel]);

    const result = await run(db, { warehouseId: baseActiveWarehouse.id });
    const { summary } = expectOk<{
      summary: { itemId: string; totalOnHand: number }[];
    }>(result as never);

    expect(summary).toHaveLength(1);
    expect(summary[0].totalOnHand).toBe(100);
  });

  it("correctly sums across multiple locations", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseActiveWarehouse);
    spies.select.mockReturnValueOnce([{ id: "location-1" }, { id: "location-2" }]);
    spies.select.mockReturnValueOnce([
      baseStockLevel,
      {
        ...baseStockLevel,
        id: "stock-level-2",
        storageLocationId: "location-2",
        onHand: 50,
        reserved: 5,
        incoming: 10,
        blocked: 0,
      },
    ]);

    const result = await run(db, { warehouseId: baseActiveWarehouse.id });
    const { summary } = expectOk<{
      summary: {
        itemId: string;
        totalOnHand: number;
        totalReserved: number;
        totalIncoming: number;
        totalBlocked: number;
        totalAvailable: number;
      }[];
    }>(result as never);

    expect(summary).toHaveLength(1);
    expect(summary[0].totalOnHand).toBe(150);
    expect(summary[0].totalReserved).toBe(15);
    expect(summary[0].totalIncoming).toBe(30);
    expect(summary[0].totalBlocked).toBe(5);
  });

  it("filters by item when provided", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseActiveWarehouse);
    spies.select.mockReturnValueOnce([{ id: "location-1" }]);
    spies.select.mockReturnValueOnce([baseStockLevel]);

    const result = await run(db, {
      warehouseId: baseActiveWarehouse.id,
      itemId: "item-1",
    });
    const { summary } = expectOk<{
      summary: { itemId: string; totalOnHand: number }[];
    }>(result as never);

    expect(summary).toHaveLength(1);
    expect(summary[0].itemId).toBe("item-1");
  });

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

    const result = await run(db, { warehouseId: "nonexistent-warehouse" });
    expectErr(result as never, WarehouseNotFoundError);
  });

  it("excludes inactive storage locations", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseActiveWarehouse);
    spies.select.mockReturnValueOnce([]);

    const result = await run(db, { warehouseId: baseActiveWarehouse.id });
    const { summary } = expectOk<{ summary: unknown[] }>(result as never);

    expect(summary).toEqual([]);
  });
});
