/**
 * install/ecosystem-commands.mjs — canonical install command per ecosystem (C54 R4).
 *
 * Every entry is a declarative {cmd, timeout_ms, needs_lockfile, requires_network}
 * record. `cmd` is an argv array (exec + args) — the detached-spawn helper
 * passes `cmd[0]` to `child_process.spawn` as the executable and
 * `cmd.slice(1)` as the argv.
 *
 * Design rules baked into the table:
 *   - Use the ECOSYSTEM'S native frozen-install flag. We do NOT try to
 *     invent a cross-ecosystem "safe install" flag matrix — security in C54
 *     comes from env isolation + URL allowlist, not from flag stripping.
 *   - `cargo fetch --locked` only, NEVER `cargo build`. build.rs is
 *     arbitrary exec; that's deferred to the verify stage.
 *   - `gradle --no-daemon` is mandatory for our invocation. Parallel
 *     worktrees would otherwise deadlock on the daemon lock file. Users
 *     can keep their daemon for interactive work; we don't kill it.
 *   - `pip-venv` is a two-step wrapper (create venv, then pip install).
 *     The detached-spawn wrapper script composes the two shell calls.
 *
 * Consumer pattern:
 *
 *     import { getInstallCommand } from '.../ecosystem-commands.mjs'
 *     const entry = getInstallCommand('pnpm')
 *     // entry.cmd === ['pnpm', 'install', '--frozen-lockfile']
 */

/**
 * @typedef {Object} EcosystemCommand
 * @property {string[]} cmd               argv (cmd[0] is exec)
 * @property {number} timeout_ms          per-install timeout (5min default)
 * @property {boolean} needs_lockfile     true ⇒ fail if lockfile missing
 * @property {boolean} requires_network   true ⇒ needs registry access
 * @property {string[]} [shell_wrapper]   optional: compose in a wrapper
 *                                        script (pip-venv is the only user)
 */

const DEFAULT_TIMEOUT_MS = 300_000 // 5 minutes per ecosystem (per spec R1.5)

/**
 * @type {Record<string, EcosystemCommand>}
 */
export const ECOSYSTEM_COMMANDS = {
	pnpm: {
		cmd: ['pnpm', 'install', '--frozen-lockfile'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: true,
		requires_network: true,
	},
	npm: {
		cmd: ['npm', 'ci'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: true,
		requires_network: true,
	},
	yarn: {
		cmd: ['yarn', 'install', '--frozen-lockfile'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: true,
		requires_network: true,
	},
	'yarn-berry': {
		cmd: ['yarn', 'install', '--immutable'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: true,
		requires_network: true,
	},
	bun: {
		cmd: ['bun', 'install', '--frozen-lockfile'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: true,
		requires_network: true,
	},
	uv: {
		cmd: ['uv', 'sync', '--frozen'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: false, // uv can resolve from pyproject.toml without uv.lock
		requires_network: true,
	},
	poetry: {
		cmd: ['poetry', 'install', '--no-root', '--sync'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: false,
		requires_network: true,
	},
	'pip-venv': {
		// Two-step wrapper: (a) python -m venv .venv (b) .venv/bin/pip install -r requirements.txt
		// detached-spawn composes these as `<venv-cmd> && <pip-cmd>` inside its wrapper
		// script. The `cmd` here is the literal shell commands that the wrapper evaluates.
		cmd: ['python', '-m', 'venv', '.venv'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: false,
		requires_network: true,
		shell_wrapper: ['python -m venv .venv', '.venv/bin/pip install -r requirements.txt'],
	},
	cargo: {
		// NEVER `cargo build` — build.rs is arbitrary exec, deferred to verify stage.
		cmd: ['cargo', 'fetch', '--locked'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: true,
		requires_network: true,
	},
	go: {
		cmd: ['go', 'mod', 'download'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: false, // go.sum is a checksum file, not the source of truth
		requires_network: true,
	},
	bundler: {
		cmd: ['bundle', 'install', '--jobs', '4', '--retry', '3'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: false, // bundler can resolve without Gemfile.lock
		requires_network: true,
	},
	composer: {
		cmd: ['composer', 'install', '--no-interaction', '--prefer-dist'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: false,
		requires_network: true,
	},
	gradle: {
		// --no-daemon mandatory: parallel worktrees deadlock on the daemon lock.
		cmd: ['./gradlew', '--no-daemon', 'dependencies'],
		timeout_ms: DEFAULT_TIMEOUT_MS,
		needs_lockfile: false,
		requires_network: true,
	},
}

/**
 * Look up the canonical install command for an ecosystem. Returns null for
 * unknown ecosystems — callers decide whether to skip or fail.
 *
 * @param {string} ecosystem
 * @returns {EcosystemCommand | null}
 */
export function getInstallCommand(ecosystem) {
	if (!ecosystem || typeof ecosystem !== 'string') return null
	const entry = ECOSYSTEM_COMMANDS[ecosystem]
	if (!entry) return null
	// Return a shallow copy so callers can mutate (e.g. timeout override)
	// without leaking back into the registry.
	return {
		cmd: [...entry.cmd],
		timeout_ms: entry.timeout_ms,
		needs_lockfile: entry.needs_lockfile,
		requires_network: entry.requires_network,
		...(entry.shell_wrapper ? { shell_wrapper: [...entry.shell_wrapper] } : {}),
	}
}

/**
 * List every supported ecosystem name.
 *
 * @returns {string[]}
 */
export function listSupportedEcosystems() {
	return Object.keys(ECOSYSTEM_COMMANDS)
}
