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

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

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

  // Validate name is non-empty
  if (!input.name || input.name.trim() === "") {
    return err(new InvalidNameError(input.hierarchyId));
  }

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

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

  let level = 0;

  if (input.parentNodeId) {
    // Self-reference check (node cannot be its own parent - relevant for logical consistency)
    // Note: for create, this is a guard against bad data passing nodeId == parentNodeId

    // Validate parent node exists and belongs to the same hierarchy
    const parentNode = await db
      .selectFrom("CostCenterHierarchyNode")
      .where("id", "=", input.parentNodeId)
      .where("hierarchyId", "=", input.hierarchyId)
      .selectAll()
      .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("CostCenterHierarchyNode")
        .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;
  } else {
    // No parent means root node — check no existing root
    const existingRoot = await db
      .selectFrom("CostCenterHierarchyNode")
      .where("hierarchyId", "=", input.hierarchyId)
      .where("parentNodeId", "is", null)
      .selectAll()
      .executeTakeFirst();

    if (existingRoot) {
      return err(new DuplicateRootNodeError(input.hierarchyId));
    }
  }

  // Create the node
  const node = await db
    .insertInto("CostCenterHierarchyNode")
    .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({ costCenterHierarchyNode: node! });
}
