import assert from "node:assert/strict";
import { test } from "vitest";
import {
	createPrintModeLivenessReporter,
	PrintModeTimeoutError,
	promptWithPrintTimeout,
	resolvePrintModeTimeoutMs,
	runPrintMode,
} from "./print-mode.js";

test("resolvePrintModeTimeoutMs_when_env_missing_returns_default_watchdog", () => {
	assert.equal(resolvePrintModeTimeoutMs({}), 55_000);
});

test("resolvePrintModeTimeoutMs_when_env_zero_disables_watchdog", () => {
	assert.equal(resolvePrintModeTimeoutMs({ SF_PRINT_TIMEOUT_MS: "0" }), 0);
});

test("promptWithPrintTimeout_when_prompt_never_settles_aborts_and_rejects", async () => {
	let aborted = false;
	const session = {
		prompt: () => new Promise<void>(() => {}),
		abort: async () => {
			aborted = true;
		},
	};

	await assert.rejects(
		() => promptWithPrintTimeout(session as any, "say hi", undefined, 5),
		PrintModeTimeoutError,
	);
	assert.equal(aborted, true);
});

test("promptWithPrintTimeout_when_prompt_settles_clears_watchdog", async () => {
	let aborted = false;
	const session = {
		prompt: async () => {},
		abort: async () => {
			aborted = true;
		},
	};

	await promptWithPrintTimeout(session as any, "say hi", undefined, 5);
	assert.equal(aborted, false);
});

test("runPrintMode_when_extension_startup_hangs_still_runs_prompt", async () => {
	const logs: string[] = [];
	let lifecycleDisabled = false;
	const originalLog = console.log;
	console.log = (message?: unknown) => {
		logs.push(String(message));
	};
	try {
		const session = {
			sessionManager: { getHeader: () => null },
			bindExtensions: async (bindings: { runLifecycle?: boolean }) => {
				lifecycleDisabled = bindings.runLifecycle === false;
			},
			subscribe: () => () => undefined,
			prompt: async () => {},
			abort: async () => {},
			state: {
				messages: [
					{
						role: "assistant",
						content: [{ type: "text", text: "hi" }],
						stopReason: "stop",
					},
				],
			},
		};

		await runPrintMode(session as any, {
			mode: "text",
			initialMessage: "say hi",
			timeoutMs: 100,
		});

		assert.deepEqual(logs, ["hi"]);
		assert.equal(lifecycleDisabled, true);
	} finally {
		console.log = originalLog;
	}
});

test("createPrintModeLivenessReporter_in_text_mode_reports_progress_to_stderr_once", () => {
	const writes: string[] = [];
	const originalWrite = process.stderr.write;
	process.stderr.write = ((chunk: string | Uint8Array) => {
		writes.push(String(chunk));
		return true;
	}) as typeof process.stderr.write;
	try {
		const report = createPrintModeLivenessReporter("text");
		report({ type: "message_start" });
		report({
			type: "message_update",
			assistantMessageEvent: { type: "thinking_start" },
		});
		report({
			type: "message_update",
			assistantMessageEvent: { type: "thinking_delta" },
		});
		report({
			type: "message_update",
			assistantMessageEvent: { type: "text_start" },
		});
		report({
			type: "message_update",
			assistantMessageEvent: { type: "text_delta" },
		});

		assert.deepEqual(writes, [
			"[forge] model responding...\n",
			"[forge] thinking...\n",
			"[forge] writing response...\n",
		]);
	} finally {
		process.stderr.write = originalWrite;
	}
});

test("createPrintModeLivenessReporter_in_json_mode_keeps_stderr_quiet", () => {
	const writes: string[] = [];
	const originalWrite = process.stderr.write;
	process.stderr.write = ((chunk: string | Uint8Array) => {
		writes.push(String(chunk));
		return true;
	}) as typeof process.stderr.write;
	try {
		const report = createPrintModeLivenessReporter("json");
		report({ type: "message_start" });
		report({
			type: "message_update",
			assistantMessageEvent: { type: "thinking_start" },
		});

		assert.deepEqual(writes, []);
	} finally {
		process.stderr.write = originalWrite;
	}
});
