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

export interface CreateProfitCenterHierarchyNodeInput {
  hierarchyId: string;
  name: string;
  parentNodeId?: string | null;
  sortOrder?: number;
}

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

  // Validate hierarchy exists
  const hierarchy = await db
    .selectFrom("ProfitCenterHierarchy")
    .selectAll()
    .where("id", "=", input.hierarchyId)
    .executeTakeFirst();

  if (!hierarchy) {
    return err(new HierarchyNotFoundError(input.hierarchyId));
  }

  const isRootNode = !input.parentNodeId;
  let level = 0;

  if (isRootNode) {
    // Check if a root node already exists in this hierarchy
    const existingRoot = await db
      .selectFrom("ProfitCenterHierarchyNode")
      .selectAll()
      .where("hierarchyId", "=", input.hierarchyId)
      .where("parentNodeId", "is", null)
      .executeTakeFirst();

    if (existingRoot) {
      return err(new DuplicateRootNodeError(input.hierarchyId));
    }
  } else {
    // Validate parent node exists within the hierarchy
    const parentNode = await db
      .selectFrom("ProfitCenterHierarchyNode")
      .selectAll()
      .where("id", "=", input.parentNodeId!)
      .where("hierarchyId", "=", input.hierarchyId)
      .executeTakeFirst();

    if (!parentNode) {
      return err(new ParentNodeNotFoundError(input.parentNodeId!));
    }

    // Check for self-reference and circular reference by walking the ancestor chain
    const visited = new Set<string>();
    let currentNodeId: string | null = input.parentNodeId!;

    while (currentNodeId !== null) {
      if (visited.has(currentNodeId)) {
        return err(new CircularReferenceError(input.parentNodeId!));
      }
      visited.add(currentNodeId);

      const currentNode = await db
        .selectFrom("ProfitCenterHierarchyNode")
        .where("id", "=", currentNodeId)
        .select(["id", "parentNodeId"])
        .executeTakeFirst();

      if (!currentNode) break;

      // Self-reference: node is its own parent
      if (currentNode.parentNodeId === currentNode.id) {
        return err(new SelfReferenceError(currentNodeId));
      }

      currentNodeId = currentNode.parentNodeId;
    }

    level = parentNode.level + 1;
  }

  // Create the node
  const node = await db
    .insertInto("ProfitCenterHierarchyNode")
    .values({
      hierarchyId: input.hierarchyId,
      name: input.name,
      parentNodeId: input.parentNodeId ?? null,
      sortOrder: input.sortOrder ?? 1,
      level,
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  return ok({ profitCenterHierarchyNode: node! });
}
