/**
 * bin-path.mjs — D-03 single source of truth for the apt-tools binary path.
 *
 * Several Layer 1 commands re-spawn `apt-tools <subcommand>` (worktree,
 * worktree-install, task) and historically each call site had its own
 * `process.argv[1]` heuristic + fallback chain. After D-03 they all go
 * through `resolveAptToolsBin(projectDir)` so the resolution lives in one
 * place and tests can override it via `APT_TOOLS_BIN_PATH`.
 *
 * Resolution order (5-step fallback chain):
 *
 *   1. process.env.APT_TOOLS_BIN_PATH      — explicit override (test seam).
 *   2. process.argv[1]                     — when invoked as the CLI entrypoint
 *                                            and the path ends with
 *                                            `apt-tools.mjs`. Vitest workers,
 *                                            REPLs, and `node -e ...` invocations
 *                                            do NOT match this branch.
 *   3. <projectDir>/packages/framework/bin/apt-tools.mjs   — monorepo path.
 *   4. <projectDir>/.aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs
 *      — consumer-install (bootstrap-script) path.
 *   5. 'apt-tools'                         — last-resort, relies on $PATH.
 *
 * Layer 1 commands MUST NOT read `process.argv[1]` directly; this module is
 * the only sanctioned consumer of that signal.
 */

import { existsSync } from 'node:fs'
import { resolve } from 'node:path'

/**
 * @param {string} projectDir
 * @returns {string} absolute path to apt-tools.mjs, or 'apt-tools' as a
 *   $PATH fallback. Never throws — callers spawn the returned path; if it
 *   doesn't resolve at exec time the spawn fails with the usual ENOENT.
 */
export function resolveAptToolsBin(projectDir) {
	// Step 1 — explicit override (used by tests and integration harnesses).
	const override = process.env.APT_TOOLS_BIN_PATH
	if (override && override.length > 0) {
		return override
	}

	// Step 2 — process.argv[1] when it points at our binary. Vitest worker
	// argv[1] is the runner, not apt-tools.mjs; REPL argv[1] is empty; only
	// real CLI invocations have argv[1] ending with `apt-tools.mjs`.
	const argv1 = process.argv[1]
	if (typeof argv1 === 'string' && argv1.endsWith('apt-tools.mjs')) {
		return argv1
	}

	// Step 3 — monorepo path under the supplied project directory.
	if (typeof projectDir === 'string' && projectDir.length > 0) {
		const monorepoBin = resolve(projectDir, 'packages/framework/bin/apt-tools.mjs')
		if (existsSync(monorepoBin)) {
			return monorepoBin
		}

		// Step 4 — consumer-install path (bootstrap script lays this layout out
		// under <projectDir>/.aperant/deps/).
		const consumerBin = resolve(
			projectDir,
			'.aperant/deps/node_modules/@aperant/framework/bin/apt-tools.mjs',
		)
		if (existsSync(consumerBin)) {
			return consumerBin
		}
	}

	// Step 5 — last-resort: rely on $PATH. The caller will surface ENOENT if
	// `apt-tools` is not on PATH.
	return 'apt-tools'
}
