import { readFile, stat } from "node:fs/promises";
import path from "node:path";

/**
 * Resolves the directories from `startPath` up to its source root, nearest
 * first. The source root is the closest ancestor that looks like a repository
 * or workspace root; a standalone directory is its own source root, so
 * discovery never escapes into unrelated parent directories.
 */
export async function resolveSourceRoot(
  startPath: string,
  signal?: AbortSignal,
): Promise<string> {
  let current = path.resolve(startPath);

  while (true) {
    if (
      (await pathExists(path.join(current, ".git"), signal)) ||
      (await pathExists(path.join(current, "pnpm-workspace.yaml"), signal)) ||
      (await pathExists(path.join(current, "bun.lock"), signal)) ||
      (await pathExists(path.join(current, "bun.lockb"), signal)) ||
      (await packageJsonDeclaresWorkspaces(current, signal))
    ) {
      return current;
    }

    const parent = path.dirname(current);
    if (parent === current) {
      return path.resolve(startPath);
    }

    current = parent;
  }
}

/** Directories from `startPath` up to its source root, nearest first. */
export async function sourceRootLineage(
  startPath: string,
  signal?: AbortSignal,
): Promise<string[]> {
  const sourceRoot = await resolveSourceRoot(startPath, signal);
  const lineage: string[] = [];
  let current = path.resolve(startPath);

  while (true) {
    lineage.push(current);
    if (current === sourceRoot) {
      return lineage;
    }

    const parent = path.dirname(current);
    if (parent === current) {
      return lineage;
    }

    current = parent;
  }
}

async function packageJsonDeclaresWorkspaces(
  directory: string,
  signal?: AbortSignal,
): Promise<boolean> {
  signal?.throwIfAborted();
  try {
    const content = await readFile(path.join(directory, "package.json"), {
      encoding: "utf8",
      signal,
    });
    const parsed = JSON.parse(content) as { workspaces?: unknown };
    return Boolean(parsed.workspaces);
  } catch (error) {
    if (signal?.aborted) throw error;
    return false;
  }
}

async function pathExists(
  targetPath: string,
  signal?: AbortSignal,
): Promise<boolean> {
  try {
    signal?.throwIfAborted();
    await stat(targetPath);
    signal?.throwIfAborted();
    return true;
  } catch (error) {
    if (signal?.aborted) throw error;
    return false;
  }
}
