import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { HierarchyNodeNotFoundError, InvalidCategoryFilterError } from "../lib/errors.generated";
import {
  baseCostCenter,
  baseCostCenter2,
  childCostCenterNode,
  childCostCenterNode2,
} from "../testing/fixtures";
import { run } from "./listCostCenters";

describe("listCostCenters", () => {
  it("returns all cost centers when no filters are provided", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([baseCostCenter, baseCostCenter2]);

    const result = await run(db, {});

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(2);
    }
  });

  it("returns cost centers filtered by companyId", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([baseCostCenter]);

    const result = await run(db, { companyId: baseCostCenter.companyId });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(1);
      expect(result.value.items[0]).toEqual(baseCostCenter);
    }
  });

  it("returns cost centers filtered by category", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([baseCostCenter]);

    const result = await run(db, { category: "overhead" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(1);
      expect(result.value.items[0].category).toEqual("overhead");
    }
  });

  it("returns cost centers filtered by hierarchyNodeId", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce({ id: childCostCenterNode.id })
      .mockReturnValueOnce([baseCostCenter]);

    const result = await run(db, { hierarchyNodeId: childCostCenterNode.id });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(1);
      expect(result.value.items[0].hierarchyNodeId).toEqual(childCostCenterNode.id);
    }
  });

  it("returns cost centers matching multiple filters simultaneously", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce({ id: childCostCenterNode2.id })
      .mockReturnValueOnce([baseCostCenter2]);

    const result = await run(db, {
      companyId: baseCostCenter2.companyId,
      hierarchyNodeId: childCostCenterNode2.id,
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toHaveLength(1);
      expect(result.value.items[0]).toEqual(baseCostCenter2);
    }
  });

  it("returns an empty list when no cost centers match the given filters", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([]);

    const result = await run(db, { companyId: "non-existent" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.items).toEqual([]);
    }
  });

  it("returns error when category filter is invalid", async () => {
    const { db } = createMockDb<DB>();
    const result = await run(db, { category: "invalid" });
    expect(result.ok).toBe(false);
    if (!result.ok) expect(result.error).toBeInstanceOf(InvalidCategoryFilterError);
  });

  it("returns error when hierarchyNodeId does not exist", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);
    const result = await run(db, { hierarchyNodeId: "missing-node" });
    expect(result.ok).toBe(false);
    if (!result.ok) expect(result.error).toBeInstanceOf(HierarchyNodeNotFoundError);
  });
});
