import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import {
  baseProfitCenter,
  baseProfitCenterHierarchy,
  childProfitCenterNode,
  rootProfitCenterNode,
} from "../testing/fixtures";
import { run } from "./getProfitCenterHierarchy";

describe("getProfitCenterHierarchy", () => {
  it("returns hierarchy with all nodes in tree structure when found by id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseProfitCenterHierarchy)
      .mockReturnValueOnce([rootProfitCenterNode, childProfitCenterNode])
      .mockReturnValueOnce([])
      .mockReturnValueOnce([baseProfitCenter]);

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

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.profitCenterHierarchy).toEqual(baseProfitCenterHierarchy);
      expect(result.value.nodes).toHaveLength(1);
      expect(result.value.nodes[0].children).toHaveLength(1);
    }
  });

  it("returns hierarchy with root node and nested child nodes", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseProfitCenterHierarchy)
      .mockReturnValueOnce([rootProfitCenterNode, childProfitCenterNode])
      .mockReturnValueOnce([])
      .mockReturnValueOnce([]);

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

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.nodes[0]).toMatchObject({
        id: rootProfitCenterNode.id,
        parentNodeId: null,
      });
      expect(result.value.nodes[0].children[0]).toMatchObject({
        id: childProfitCenterNode.id,
        parentNodeId: rootProfitCenterNode.id,
      });
    }
  });

  it("returns hierarchy nodes with assigned profit centers populated", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseProfitCenterHierarchy)
      .mockReturnValueOnce([childProfitCenterNode])
      .mockReturnValueOnce([baseProfitCenter]);

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

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.nodes[0]).toMatchObject({ id: childProfitCenterNode.id });
      expect(result.value.nodes[0].profitCenters).toEqual([baseProfitCenter]);
    }
  });

  it("returns leaf nodes with no children", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce(baseProfitCenterHierarchy)
      .mockReturnValueOnce([childProfitCenterNode])
      .mockReturnValueOnce([]);

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

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

  it("returns null when no hierarchy matches the given id", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);

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

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