import type { Transaction } from "../generated/kysely-tailordb";
import {
  NodeNotFoundError,
  ParentNodeNotFoundError,
  SelfReferenceError,
  CircularReferenceError,
} from "../lib/errors.generated";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface MoveCostCenterHierarchyNodeInput {
  nodeId: string;
  newParentNodeId: string;
}

export async function run(
  db: Transaction,
  input: MoveCostCenterHierarchyNodeInput,
  ctx: CommandContext,
) {
  void ctx;

  // Self-reference check
  if (input.nodeId === input.newParentNodeId) {
    return err(new SelfReferenceError(input.nodeId));
  }

  // Validate node exists
  const node = await db
    .selectFrom("CostCenterHierarchyNode")
    .where("id", "=", input.nodeId)
    .selectAll()
    .forUpdate()
    .executeTakeFirst();

  if (!node) {
    return err(new NodeNotFoundError(input.nodeId));
  }

  // Validate new parent exists and is in the same hierarchy
  const newParent = await db
    .selectFrom("CostCenterHierarchyNode")
    .where("id", "=", input.newParentNodeId)
    .where("hierarchyId", "=", node.hierarchyId)
    .selectAll()
    .executeTakeFirst();

  if (!newParent) {
    return err(new ParentNodeNotFoundError(input.newParentNodeId));
  }

  // Circular reference check: walk up from new parent to root,
  // ensuring the node-to-move is not an ancestor of the new parent
  let current: { parentNodeId: string | null } | undefined = newParent;
  while (current?.parentNodeId) {
    if (current.parentNodeId === input.nodeId) {
      return err(new CircularReferenceError(input.nodeId));
    }
    current = await db
      .selectFrom("CostCenterHierarchyNode")
      .where("id", "=", current.parentNodeId)
      .selectAll()
      .executeTakeFirst();
  }

  // Compute new level
  const newLevel = newParent.level + 1;

  // Update the node
  const updatedNode = await db
    .updateTable("CostCenterHierarchyNode")
    .set({
      parentNodeId: input.newParentNodeId,
      level: newLevel,
      updatedAt: new Date(),
    })
    .where("id", "=", input.nodeId)
    .returningAll()
    .executeTakeFirst();

  return ok({ costCenterHierarchyNode: updatedNode! });
}
