/**
 * Tests for autonomous routing — verifies that `autonomous` is routed to
 * machine surface so it doesn't fall through to the interactive TUI, which hangs
 * when stdin/stdout are piped.
 *
 * Regression test for #2732.
 */

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { test } from "vitest";

const projectRoot = join(fileURLToPath(import.meta.url), "..", "..", "..");

// ---------------------------------------------------------------------------
// Source-level verification: cli.ts must handle autonomous before TUI
// ---------------------------------------------------------------------------

/**
 * Read cli.ts and verify the autonomous subcommand is routed before the
 * interactive TUI code path. This is the definitive test — if cli.ts doesn't
 * handle autonomous mode, piped invocations will hang (#2732).
 */
function cliSourceHandlesAutonomousBeforeTUI(): boolean {
	const cliSource = readFileSync(join(projectRoot, "src", "cli.ts"), "utf-8");

	// Find the position of the autonomous subcommand handler.
	const autoHandlerMatch = cliSource.match(
		/messages\[0\]\s*===\s*['"]autonomous['"]/,
	);
	if (!autoHandlerMatch) return false;

	// Find the position of the InteractiveMode TUI entry
	const tuiMatch = cliSource.match(/new\s+InteractiveMode\s*\(/);
	if (!tuiMatch) return false;

	// The autonomous handler must appear BEFORE the TUI in the source
	const autoPos = cliSource.indexOf(autoHandlerMatch[0]);
	const tuiPos = cliSource.indexOf(tuiMatch[0]);

	return autoPos < tuiPos;
}

// ═══════════════════════════════════════════════════════════════════════════
// Core regression test: `sf autonomous` must be handled before TUI (#2732)
// ═══════════════════════════════════════════════════════════════════════════

test("cli.ts handles `autonomous` subcommand before interactive TUI (#2732)", () => {
	assert.ok(
		cliSourceHandlesAutonomousBeforeTUI(),
		'cli.ts must route messages[0] === "autonomous" to a handler BEFORE ' +
			"reaching `new InteractiveMode()`. Without this, `sf autonomous` with " +
			"piped stdin/stdout falls through to the TUI and hangs.",
	);
});

// ═══════════════════════════════════════════════════════════════════════════
// Verify the autonomous handler routes to headless (not a stub/no-op)
// ═══════════════════════════════════════════════════════════════════════════

test("cli.ts routes `autonomous` to headless runner", () => {
	const cliSource = readFileSync(join(projectRoot, "src", "cli.ts"), "utf-8");

	// The autonomous handler block should import or reference headless.
	const autoBlockRegex =
		/messages\[0\]\s*===\s*['"]autonomous['"][\s\S]*?runHeadless/;
	assert.ok(
		autoBlockRegex.test(cliSource),
		"`autonomous` subcommand handler must invoke runHeadless to delegate to machine surface",
	);
});

// ═══════════════════════════════════════════════════════════════════════════
// Verify piped-mode hint in error message when autonomous mode is not available
// ═══════════════════════════════════════════════════════════════════════════

test("TTY error message mentions non-interactive alternatives", () => {
	const cliSource = readFileSync(join(projectRoot, "src", "cli.ts"), "utf-8");

	assert.ok(
		cliSource.includes("sf autonomous") || cliSource.includes("sf headless"),
		"TTY error hints should mention headless/autonomous mode as alternatives",
	);
});

// ═══════════════════════════════════════════════════════════════════════════
// `sf headless` still works (no regression)
// ═══════════════════════════════════════════════════════════════════════════

test("cli.ts handles `headless` subcommand before interactive TUI", () => {
	const cliSource = readFileSync(join(projectRoot, "src", "cli.ts"), "utf-8");

	const headlessMatch = cliSource.match(
		/messages\[0\]\s*===\s*['"]headless['"]/,
	);
	const tuiMatch = cliSource.match(/new\s+InteractiveMode\s*\(/);

	assert.ok(headlessMatch, "headless subcommand handler exists");
	assert.ok(tuiMatch, "InteractiveMode TUI exists");

	const headlessPos = cliSource.indexOf(headlessMatch![0]);
	const tuiPos = cliSource.indexOf(tuiMatch![0]);
	assert.ok(headlessPos < tuiPos, "headless handler is before TUI");
});
