import type { DB } from "../generated/kysely-tailordb";
import { ok, type ReadonlyDB } from "@tailor-platform/erp-kit/module";

export interface GetCostCenterHierarchyInput {
  id: string;
}

type CostCenterHierarchyTreeNode = Awaited<ReturnType<typeof loadNodesWithCostCenters>>[number] & {
  children: CostCenterHierarchyTreeNode[];
};

async function loadNodesWithCostCenters(db: ReadonlyDB<DB>, hierarchyId: string) {
  const nodes = await db
    .selectFrom("CostCenterHierarchyNode")
    .selectAll()
    .where("hierarchyId", "=", hierarchyId)
    .orderBy("level", "asc")
    .orderBy("sortOrder", "asc")
    .execute();

  return Promise.all(
    nodes.map(async (node) => {
      const costCenters = await db
        .selectFrom("CostCenter")
        .selectAll()
        .where("hierarchyNodeId", "=", node.id)
        .execute();
      return { ...node, costCenters };
    }),
  );
}

function buildTree<T extends { id: string; parentNodeId: string | null }>(
  nodes: T[],
): (T & { children: (T & { children: unknown[] })[] })[] {
  const nodeMap = new Map(
    nodes.map((node) => [node.id, { ...node, children: [] as (T & { children: unknown[] })[] }]),
  );
  const roots: (T & { children: (T & { children: unknown[] })[] })[] = [];

  for (const node of nodes) {
    const treeNode = nodeMap.get(node.id)!;
    if (!node.parentNodeId) {
      roots.push(treeNode);
      continue;
    }

    const parent = nodeMap.get(node.parentNodeId);
    if (parent) {
      parent.children.push(treeNode);
    } else {
      roots.push(treeNode);
    }
  }

  return roots;
}

/**
 * Function: getCostCenterHierarchy
 *
 * Retrieves a single cost center hierarchy by id, along with its hierarchy nodes
 * and the cost centers assigned to each node.
 */
export async function run(db: ReadonlyDB<DB>, input: GetCostCenterHierarchyInput) {
  const hierarchy = await db
    .selectFrom("CostCenterHierarchy")
    .selectAll()
    .where("id", "=", input.id)
    .executeTakeFirst();

  if (!hierarchy) {
    return ok({ costCenterHierarchy: null, nodes: [] });
  }

  const nodesWithCostCenters = await loadNodesWithCostCenters(db, input.id);
  const nodes = buildTree(nodesWithCostCenters) as CostCenterHierarchyTreeNode[];

  return ok({ costCenterHierarchy: hierarchy, nodes });
}
