#!/usr/bin/env node

/**
 * apt-tools — Deterministic CLI kernel for @aperant/framework
 *
 * Handles operations that the LLM should never do itself (file I/O, git, state).
 * All output is JSON to stdout. No external dependencies — pure Node.js.
 *
 * Usage: apt-tools <command> [args]
 *
 * Layer 3 of the post-refactor architecture (D-04). Owns: argv parsing,
 * gitignore-drift warning, --help / --version, and the IS_CLI_ENTRYPOINT
 * guard. The DISPATCH map + wrap() helper live in
 * ../src/cli/dispatch.mjs; every command body lives under
 * ../src/cli/commands/{command}.mjs.
 *
 * The `parseAcceptanceCriteria` / `resolveSpecPath` re-exports below preserve
 * the test-time import surface that existed before the C29/C30 modularization
 * refactors. Canonical home (C30): src/cli/coverage-check/acceptance-criteria.mjs.
 */

import { realpathSync } from 'node:fs'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { emitDriftWarningIfAny } from '../src/cli/config/gitignore-drift.mjs'
import { maybeSweepShippedMergedTasks } from '../src/cli/config/post-merge-sweep.mjs'
import { DISPATCH } from '../src/cli/dispatch.mjs'
import { printHelp } from '../src/cli/help.mjs'
import { fail, getVersion } from '../src/cli/util/io.mjs'

// When imported (e.g. from a unit test via `await import(...)`) we want the
// named helper exports to be usable without the CLI dispatch at the bottom of
// this file taking over the test runner's argv. This flag gates the dispatch.
const IS_CLI_ENTRYPOINT = (() => {
	try {
		if (!process.argv[1]) return false
		const invokedPath = realpathSync(process.argv[1])
		const currentPath = realpathSync(fileURLToPath(import.meta.url))
		return pathToFileURL(currentPath).href === pathToFileURL(invokedPath).href
	} catch {
		return false
	}
})()

// ---------------------------------------------------------------------------
// Named exports — for in-process unit testing of pure helpers.
// Keep surface area minimal: only helpers that are useful in isolation.
// ---------------------------------------------------------------------------

export {
	parseAcceptanceCriteria,
	resolveSpecPath,
} from '../src/cli/coverage-check/acceptance-criteria.mjs'

// ---------------------------------------------------------------------------
// Dispatch
// ---------------------------------------------------------------------------

// Only run the CLI dispatch when this file is invoked as the entrypoint.
// When imported by tests (`await import('./apt-tools.mjs')`) we skip dispatch
// so the test runner's argv does not get interpreted as an apt-tools command.
if (IS_CLI_ENTRYPOINT) {
	const args = process.argv.slice(2)
	const command = args[0]

	// C34a — non-blocking drift warning when `.aperant/.gitignore` no longer
	// matches the policy-expected template. Skipped for hot-path commands,
	// silenced by `APT_SUPPRESS_GITIGNORE_DRIFT=1`, never throws.
	//
	// BUG-028: emitDriftWarningIfAny internally resolves the shared (main-repo)
	// root via git/worktree-detect.mjs so the warning fires on the same file
	// the ship-gate `gitignore-in-sync` inspects (`ctx.sharedRoot`). The
	// `bin/apt-tools.mjs` entrypoint MUST NOT import from `coordination/` —
	// that seam is @internal (R8). The shared-root resolution lives inside
	// `gitignore-drift.mjs` to keep the seam intact.
	try {
		emitDriftWarningIfAny(process.cwd(), command)
	} catch {
		/* never block user command on drift check */
	}

	// C57 Piece 2 — passive post-merge sweep. On every dispatch (modulo
	// allowlist + cooldown + recursion guards), check if any task is stuck
	// in `shipped-pending-merge` and spawn a detached `task close-merged`
	// child to poll `gh pr view` and clean them up. Same fail-soft contract
	// as the gitignore-drift hook: silenced by APT_SUPPRESS_MERGE_SWEEP=1,
	// recursion-guarded by APT_INTERNAL_MERGE_SWEEP=1, never throws.
	try {
		maybeSweepShippedMergedTasks(process.cwd(), command)
	} catch {
		/* never block user command on merge sweep */
	}

	if (!command || command === '--help' || command === '-h') {
		printHelp()
		process.exit(0)
	}

	if (command === '--version' || command === '-v') {
		process.stdout.write(getVersion() + '\n')
		process.exit(0)
	}

	const handler = DISPATCH[command]
	if (handler) {
		// All handlers return a Promise. wrap() handles output(),
		// process.exit(), and synchronous-error → fail() conversion. Top-level
		// .catch keeps unhandled rejections out of the event loop.
		handler(args).catch((err) => {
			fail(err && err.message ? err.message : String(err))
		})
	} else {
		fail(`Unknown command: ${command}. Run apt-tools --help for usage.`)
	}
} // end if (IS_CLI_ENTRYPOINT)
