/**
 * Tests for autonomous mode piped I/O detection (#2732).
 *
 * When `sf autonomous` is run with piped stdout (e.g. `sf autonomous | cat`),
 * the CLI should detect the non-TTY stdout and redirect to headless
 * mode instead of hanging in interactive mode trying to set up a TUI
 * on a non-terminal output stream.
 *
 * Also verifies the stdout TTY gate at the interactive mode entry point:
 * when stdout is piped, interactive mode must not be entered regardless
 * of the subcommand.
 */

import assert from "node:assert/strict";
import { test } from "vitest";

// ─── Extracted detection logic (mirrors cli.ts) ───────────────────────────

/**
 * Subcommands that are explicitly handled before the interactive mode
 * section in cli.ts and therefore never fall through to the TUI.
 */
const EXPLICIT_SUBCOMMANDS = new Set([
	"headless",
	"update",
	"config",
	"worktree",
	"wt",
	"sessions",
	"web",
]);

/**
 * Detect whether the current subcommand should be redirected
 * to machine surface when stdout is not a TTY.
 *
 * Returns true when the subcommand is "autonomous" and stdout is piped.
 */
function shouldRedirectAutonomousToHeadless(
	subcommand: string | undefined,
	stdoutIsTTY: boolean,
): boolean {
	if (stdoutIsTTY) return false;
	return subcommand === "autonomous";
}

/**
 * Check whether interactive mode can be entered.
 * Both stdin AND stdout must be TTY for the TUI to work.
 */
function canEnterInteractiveMode(
	stdinIsTTY: boolean,
	stdoutIsTTY: boolean,
): boolean {
	return stdinIsTTY && stdoutIsTTY;
}

/**
 * Returns true if the subcommand is handled by an explicit branch
 * in cli.ts and will never reach the interactive mode section.
 */
function isExplicitSubcommand(subcommand: string | undefined): boolean {
	return subcommand !== undefined && EXPLICIT_SUBCOMMANDS.has(subcommand);
}

// ─── shouldRedirectAutonomousToHeadless ─────────────────────────────────────────

test("redirects 'autonomous' to headless when stdout is piped", () => {
	assert.ok(shouldRedirectAutonomousToHeadless("autonomous", false));
});

test("does NOT redirect non-autonomous subcommands when stdout is piped", () => {
	assert.ok(!shouldRedirectAutonomousToHeadless("auto", false));
	assert.ok(!shouldRedirectAutonomousToHeadless("headless", false));
	assert.ok(!shouldRedirectAutonomousToHeadless("config", false));
	assert.ok(!shouldRedirectAutonomousToHeadless("update", false));
	assert.ok(!shouldRedirectAutonomousToHeadless(undefined, false));
});

// ─── canEnterInteractiveMode ──────────────────────────────────────────────

test("allows interactive mode when both stdin and stdout are TTY", () => {
	assert.ok(canEnterInteractiveMode(true, true));
});

test("blocks interactive mode when stdin is piped", () => {
	assert.ok(!canEnterInteractiveMode(false, true));
});

test("blocks interactive mode when stdout is piped", () => {
	assert.ok(!canEnterInteractiveMode(true, false));
});

test("blocks interactive mode when both stdin and stdout are piped", () => {
	assert.ok(!canEnterInteractiveMode(false, false));
});

// ─── isExplicitSubcommand ─────────────────────────────────────────────────

test("identifies explicitly handled subcommands", () => {
	assert.ok(isExplicitSubcommand("headless"));
	assert.ok(isExplicitSubcommand("update"));
	assert.ok(isExplicitSubcommand("config"));
	assert.ok(isExplicitSubcommand("worktree"));
	assert.ok(isExplicitSubcommand("wt"));
	assert.ok(isExplicitSubcommand("sessions"));
	assert.ok(isExplicitSubcommand("web"));
});

test("does NOT identify 'auto' as explicit subcommand", () => {
	assert.ok(!isExplicitSubcommand("auto"));
});

test("does NOT identify undefined as explicit subcommand", () => {
	assert.ok(!isExplicitSubcommand(undefined));
});

// ─── End-to-end scenario: sf autonomous | cat ────────────────────────────

test("scenario: 'sf autonomous 2>&1 | cat' — should redirect to headless", () => {
	const subcommand = "autonomous";
	const stdinIsTTY = true;
	const stdoutIsTTY = false;

	// Interactive mode should be blocked
	assert.ok(!canEnterInteractiveMode(stdinIsTTY, stdoutIsTTY));

	assert.ok(shouldRedirectAutonomousToHeadless(subcommand, stdoutIsTTY));
});

test("scenario: 'sf autonomous > /tmp/output.txt' — should redirect to headless", () => {
	const subcommand = "autonomous";
	const stdinIsTTY = true;
	const stdoutIsTTY = false;

	assert.ok(!canEnterInteractiveMode(stdinIsTTY, stdoutIsTTY));
	assert.ok(shouldRedirectAutonomousToHeadless(subcommand, stdoutIsTTY));
});

test("scenario: 'sf autonomous' in terminal — normal interactive mode", () => {
	const subcommand = "autonomous";
	const stdinIsTTY = true;
	const stdoutIsTTY = true;

	assert.ok(canEnterInteractiveMode(stdinIsTTY, stdoutIsTTY));
	assert.ok(!shouldRedirectAutonomousToHeadless(subcommand, stdoutIsTTY));
});

test("scenario: 'echo msg | sf autonomous' — stdin piped, should redirect", () => {
	const subcommand = "autonomous";
	const stdinIsTTY = false;
	const stdoutIsTTY = true; // stdout is TTY even though stdin is piped

	// stdout is TTY, so autonomous redirect doesn't trigger...
	assert.ok(!shouldRedirectAutonomousToHeadless(subcommand, stdoutIsTTY));
	// ...but interactive mode is blocked because stdin is piped
	assert.ok(!canEnterInteractiveMode(stdinIsTTY, stdoutIsTTY));
});

test("scenario: 'echo msg | sf autonomous | cat' — both piped", () => {
	const subcommand = "autonomous";
	const stdinIsTTY = false;
	const stdoutIsTTY = false;

	assert.ok(!canEnterInteractiveMode(stdinIsTTY, stdoutIsTTY));
	assert.ok(shouldRedirectAutonomousToHeadless(subcommand, stdoutIsTTY));
});
