import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import { Transaction } from "../generated/kysely-tailordb";
import {
  DuplicateNodeCodeError,
  MaxDepthExceededError,
  ParentNodeNotFoundError,
} from "../lib/errors.generated";

export interface CreateTaxonomyNodeInput {
  code: string;
  name: string;
  parentId?: string;
}

/**
 * Function: createTaxonomyNode
 *
 * Creates a new root or child taxonomy node with a unique code.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreateTaxonomyNodeInput & CF,
  _ctx: CommandContext,
  deps: { maxDepth: number },
) {
  const { maxDepth } = deps;
  const { code, name, parentId, ...customFields } = input;

  // 1. Check code uniqueness (uniqueness before insert — lock)
  const existingCode = await db
    .selectFrom("TaxonomyNode")
    .selectAll()
    .where("code", "=", code)
    .forUpdate()
    .executeTakeFirst();

  if (existingCode) {
    return err(new DuplicateNodeCodeError(code));
  }

  // 2. If parent specified, check it exists and validate depth
  if (parentId) {
    const parent = await db
      .selectFrom("TaxonomyNode")
      .selectAll()
      .where("id", "=", parentId)
      .executeTakeFirst();

    if (!parent) {
      return err(new ParentNodeNotFoundError(parentId));
    }

    // Calculate depth: walk up from parent to root
    let depth = 1;
    let currentId: string | null = parentId;
    while (currentId) {
      const node = await db
        .selectFrom("TaxonomyNode")
        .selectAll()
        .where("id", "=", currentId)
        .executeTakeFirst();
      if (!node?.parentId) break;
      depth++;
      currentId = node.parentId;
    }
    // Add 1 for the new node
    const newNodeDepth = depth + 1;

    if (newNodeDepth > maxDepth) {
      return err(new MaxDepthExceededError(`${newNodeDepth} exceeds ${maxDepth}`));
    }
  }

  // 3. Create node
  const node = await db
    .insertInto("TaxonomyNode")
    .values({
      ...(customFields as Record<string, unknown>),
      code,
      name,
      parentId: parentId ?? null,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ taxonomyNode: node });
}
