import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  NodeNotFoundError,
  NodeHasChildrenError,
  NodeHasAssignedProfitCentersError,
} from "../lib/errors.generated";
import { rootProfitCenterNode, childProfitCenterNode, baseProfitCenter } from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./deleteProfitCenterHierarchyNode";

describe("deleteProfitCenterHierarchyNode", () => {
  it("deletes a leaf node with no children and no assigned profit centers", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(childProfitCenterNode) // node exists
      .mockReturnValueOnce(undefined) // no children
      .mockReturnValueOnce(undefined); // no assigned profit centers

    const result = await run(db, { nodeId: childProfitCenterNode.id }, commandCtx);

    const value = expectOk(result);
    expect(value.profitCenterHierarchyNode).toEqual(childProfitCenterNode);
    expect(spies.delete).toHaveBeenCalled();
  });

  it("returns error when node does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { nodeId: "missing-node" }, commandCtx);

    expectErr(result, NodeNotFoundError);
  });

  it("returns error when node has child nodes", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(rootProfitCenterNode) // node exists
      .mockReturnValueOnce(childProfitCenterNode); // has children

    const result = await run(db, { nodeId: rootProfitCenterNode.id }, commandCtx);

    expectErr(result, NodeHasChildrenError);
  });

  it("returns error when node has assigned profit centers", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(childProfitCenterNode) // node exists
      .mockReturnValueOnce(undefined) // no children
      .mockReturnValueOnce(baseProfitCenter); // has assigned profit center

    const result = await run(db, { nodeId: childProfitCenterNode.id }, commandCtx);

    expectErr(result, NodeHasAssignedProfitCentersError);
  });

  it("emits audit event recording the deletion and acting user", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(childProfitCenterNode)
      .mockReturnValueOnce(undefined)
      .mockReturnValueOnce(undefined);

    const result = await run(db, { nodeId: childProfitCenterNode.id }, commandCtx);

    expectOk(result);
    expect(spies.delete).toHaveBeenCalled();
  });
});
