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

export interface GetProfitCenterHierarchyInput {
  id: string;
}

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

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

  return Promise.all(
    nodes.map(async (node) => {
      const profitCenters = await db
        .selectFrom("ProfitCenterHierarchyAssignment")
        .innerJoin(
          "ProfitCenter",
          "ProfitCenter.id",
          "ProfitCenterHierarchyAssignment.profitCenterId",
        )
        .selectAll("ProfitCenter")
        .where("ProfitCenterHierarchyAssignment.hierarchyNodeId", "=", node.id)
        .execute();
      return { ...node, profitCenters };
    }),
  );
}

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: getProfitCenterHierarchy
 *
 * Retrieves a single profit center hierarchy by id, along with its hierarchy nodes
 * and the profit centers assigned to each node.
 */
export async function run(db: ReadonlyDB<DB>, input: GetProfitCenterHierarchyInput) {
  const hierarchy = await db
    .selectFrom("ProfitCenterHierarchy")
    .selectAll()
    .where("id", "=", input.id)
    .executeTakeFirst();

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

  const nodesWithProfitCenters = await loadNodesWithProfitCenters(db, input.id);
  const nodes = buildTree(nodesWithProfitCenters) as ProfitCenterHierarchyTreeNode[];

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