import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  CostCenterNotFoundError,
  HierarchyNodeNotFoundError,
  CompanyMismatchError,
} from "../lib/errors.generated";
import {
  baseCostCenter,
  childCostCenterNode,
  childCostCenterNode2,
  baseCostCenterHierarchy,
} from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./assignCostCenterToHierarchyNode";

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

    const result = await run(
      db,
      { costCenterId: "missing-cc", hierarchyNodeId: childCostCenterNode.id },
      commandCtx,
    );

    expectErr(result, CostCenterNotFoundError);
  });

  it("returns error when target hierarchy node does not exist", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(baseCostCenter) // cost center exists
      .mockReturnValueOnce(undefined); // target node not found

    const result = await run(
      db,
      { costCenterId: baseCostCenter.id, hierarchyNodeId: "missing-node" },
      commandCtx,
    );

    expectErr(result, HierarchyNodeNotFoundError);
  });

  it("returns error when cost center and node belong to different companies", async () => {
    const otherCompanyHierarchy = { ...baseCostCenterHierarchy, companyId: "other-company" };

    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(baseCostCenter) // cost center exists
      .mockReturnValueOnce(childCostCenterNode2) // target node exists
      .mockReturnValueOnce(otherCompanyHierarchy); // hierarchy has different company

    const result = await run(
      db,
      { costCenterId: baseCostCenter.id, hierarchyNodeId: childCostCenterNode2.id },
      commandCtx,
    );

    expectErr(result, CompanyMismatchError);
  });

  it("assigns cost center to hierarchy node successfully", async () => {
    const updatedCostCenter = {
      ...baseCostCenter,
      hierarchyNodeId: childCostCenterNode2.id,
      updatedAt: new Date(),
    };

    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(baseCostCenter) // cost center exists
      .mockReturnValueOnce(childCostCenterNode2) // target node exists
      .mockReturnValueOnce(baseCostCenterHierarchy); // same company hierarchy
    spies.update.mockReturnValueOnce(updatedCostCenter);

    const result = await run(
      db,
      { costCenterId: baseCostCenter.id, hierarchyNodeId: childCostCenterNode2.id },
      commandCtx,
    );

    const value = expectOk(result);
    expect(value.costCenter.hierarchyNodeId).toBe(childCostCenterNode2.id);
  });

  it("updates the hierarchyNodeId field on the cost center", async () => {
    const updatedCostCenter = {
      ...baseCostCenter,
      hierarchyNodeId: childCostCenterNode2.id,
      updatedAt: new Date(),
    };

    const { db, spies } = createMockDb<Transaction>();
    spies.select
      .mockReturnValueOnce(baseCostCenter)
      .mockReturnValueOnce(childCostCenterNode2)
      .mockReturnValueOnce(baseCostCenterHierarchy);
    spies.update.mockReturnValueOnce(updatedCostCenter);

    const result = await run(
      db,
      { costCenterId: baseCostCenter.id, hierarchyNodeId: childCostCenterNode2.id },
      commandCtx,
    );

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