import type { InteractiveShellResult, HandsFreeUpdate, MonitorEventPayload, MonitorSessionState } from "./types.js";
import type { HeadlessCompletionInfo } from "./headless-monitor.js";
import { formatDurationMs } from "./types.js";

const BRIEF_TAIL_LINES = 5;

export function buildDispatchNotification(sessionId: string, info: HeadlessCompletionInfo, duration: string): string {
	const parts = [buildDispatchStatusLine(sessionId, info, duration)];
	if (info.completionOutput && info.completionOutput.totalLines > 0) {
		parts.push(` ${info.completionOutput.totalLines} lines of output.`);
	}
	appendTailBlock(parts, info.completionOutput?.lines, BRIEF_TAIL_LINES);
	parts.push(`\n\nAttach to review full output: interactive_shell({ attach: "${sessionId}" })`);
	return parts.join("");
}

export function buildResultNotification(sessionId: string, result: InteractiveShellResult): string {
	const parts = [buildResultStatusLine(sessionId, result)];
	if (result.completionOutput && result.completionOutput.lines.length > 0) {
		const truncNote = result.completionOutput.truncated
			? ` (truncated from ${result.completionOutput.totalLines} total lines)`
			: "";
		parts.push(`\nOutput (${result.completionOutput.lines.length} lines${truncNote}):\n\n${result.completionOutput.lines.join("\n")}`);
	}
	return parts.join("");
}

export function buildMonitorEventNotification(event: MonitorEventPayload): string {
	return [
		`Monitor Event (${event.sessionId}) #${event.eventId}`,
		`Time: ${event.timestamp}`,
		`Strategy: ${event.strategy}`,
		`Trigger: ${event.triggerId}`,
		`Matched: ${event.matchedText}`,
		`${event.strategy === "poll-diff" ? "Diff" : "Line"}: ${event.lineOrDiff}`,
	].join("\n");
}

export function buildMonitorLifecycleNotification(state: MonitorSessionState): string {
	const reason = state.terminalReason ?? "stopped";
	let headline: string;
	if (reason === "stream-ended") {
		headline = `Monitor ${state.sessionId} stream ended.`;
	} else if (reason === "timed-out") {
		headline = `Monitor ${state.sessionId} timed out.`;
	} else if (reason === "script-failed") {
		headline = `Monitor ${state.sessionId} script failed.`;
	} else {
		headline = `Monitor ${state.sessionId} stopped.`;
	}

	const details: string[] = [
		headline,
		`Strategy: ${state.strategy}`,
		`Events: ${state.eventCount}`,
		state.lastEventAt ? `Last event: #${state.lastEventId} at ${state.lastEventAt}` : "Last event: none",
	];

	if (state.exitCode !== undefined && state.exitCode !== null) {
		details.push(`Exit code: ${state.exitCode}`);
	}
	if (state.signal !== undefined) {
		details.push(`Signal: ${state.signal}`);
	}

	return details.join("\n");
}

export function buildHandsFreeUpdateMessage(update: HandsFreeUpdate): { content: string; details: HandsFreeUpdate } | null {
	if (update.status === "running") return null;

	const tail = update.tail.length > 0 ? `\n\n${update.tail.join("\n")}` : "";
	let statusLine: string;
	switch (update.status) {
		case "exited":
			statusLine = `Session ${update.sessionId} exited (${formatDurationMs(update.runtime)})`;
			break;
		case "killed":
			statusLine = `Session ${update.sessionId} killed (${formatDurationMs(update.runtime)})`;
			break;
		case "user-takeover":
			statusLine = `Session ${update.sessionId}: user took over (${formatDurationMs(update.runtime)})`;
			break;
		case "agent-resumed":
			statusLine = `Session ${update.sessionId}: agent resumed monitoring (${formatDurationMs(update.runtime)})`;
			break;
		default:
			statusLine = `Session ${update.sessionId} update (${formatDurationMs(update.runtime)})`;
	}
	return { content: statusLine + tail, details: update };
}

export function summarizeInteractiveResult(command: string, result: InteractiveShellResult, timeout?: number, reason?: string): string {
	let summary = buildInteractiveSummary(result, timeout);

	if (result.userTookOver) {
		summary += "\n\nNote: User took over control during hands-free mode.";
	}

	if (!result.transferred && result.handoffPreview?.type === "tail" && result.handoffPreview.lines.length > 0) {
		summary += `\n\nOverlay tail (${result.handoffPreview.when}, last ${result.handoffPreview.lines.length} lines):\n${result.handoffPreview.lines.join("\n")}`;
	}

	const warning = buildIdlePromptWarning(command, reason);
	if (warning) {
		summary += `\n\n${warning}`;
	}

	return summary;
}

export function buildIdlePromptWarning(command: string, reason: string | undefined): string | null {
	if (!reason) return null;

	const tasky = /\b(scan|check|review|summariz|analyz|inspect|audit|find|fix|refactor|debug|investigat|explore|enumerat|list)\b/i;
	if (!tasky.test(reason)) return null;

	const trimmed = command.trim();
	const binaries = ["pi", "claude", "codex", "gemini", "agent"] as const;
	const bin = binaries.find((candidate) => trimmed === candidate || trimmed.startsWith(`${candidate} `));
	if (!bin) return null;

	const rest = trimmed === bin ? "" : trimmed.slice(bin.length).trim();
	const hasQuotedPrompt = /["']/.test(rest);
	const hasKnownPromptFlag =
		/\b(-p|--print|--prompt|--prompt-interactive|-i|exec)\b/.test(rest) ||
		(bin === "pi" && /\b-p\b/.test(rest)) ||
		(bin === "codex" && /\bexec\b/.test(rest));

	if (hasQuotedPrompt || hasKnownPromptFlag) return null;
	if (!looksLikeIdleCommand(rest)) return null;

	const examplePrompt = reason.replace(/\s+/g, " ").trim();
	const clipped = examplePrompt.length > 120 ? `${examplePrompt.slice(0, 117)}...` : examplePrompt;
	return `Note: \`reason\` is UI-only. This command likely started the agent idle. If you intended an initial prompt, embed it in \`command\`, e.g. \`${bin} "${clipped}"\`.`;
}

function buildDispatchStatusLine(sessionId: string, info: HeadlessCompletionInfo, duration: string): string {
	if (info.timedOut) return `Session ${sessionId} timed out (${duration}).`;
	if (info.cancelled) return `Session ${sessionId} was killed (${duration}).`;
	if (info.exitCode === 0) return `Session ${sessionId} completed successfully (${duration}).`;
	return `Session ${sessionId} exited with code ${info.exitCode} (${duration}).`;
}

function buildResultStatusLine(sessionId: string, result: InteractiveShellResult): string {
	if (result.timedOut) return `Session ${sessionId} timed out.`;
	if (result.cancelled) return `Session ${sessionId} was killed.`;
	if (result.exitCode === 0) return `Session ${sessionId} completed successfully.`;
	return `Session ${sessionId} exited with code ${result.exitCode}.`;
}

function buildInteractiveSummary(result: InteractiveShellResult, timeout?: number): string {
	if (result.transferred) {
		const truncatedNote = result.transferred.truncated ? ` (truncated from ${result.transferred.totalLines} total lines)` : "";
		return `Session output transferred (${result.transferred.lines.length} lines${truncatedNote}):\n\n${result.transferred.lines.join("\n")}`;
	}
	if (result.backgrounded) {
		return `Session running in background (id: ${result.backgroundId}). User can reattach with /attach ${result.backgroundId}`;
	}
	if (result.cancelled) return "User killed the interactive session";
	if (result.timedOut) return `Session killed after timeout (${timeout ?? "?"}ms)`;
	const status = result.exitCode === 0 ? "successfully" : `with code ${result.exitCode}`;
	return `Session ended ${status}`;
}

function appendTailBlock(parts: string[], lines: string[] | undefined, tailLines: number): void {
	if (!lines || lines.length === 0) return;
	let end = lines.length;
	while (end > 0 && lines[end - 1].trim() === "") end--;
	const tail = lines.slice(Math.max(0, end - tailLines), end);
	if (tail.length > 0) {
		parts.push(`\n\n${tail.join("\n")}`);
	}
}

function looksLikeIdleCommand(rest: string): boolean {
	return rest.length === 0 || /^(-{1,2}[A-Za-z0-9][A-Za-z0-9-]*(?:=[^\s]+|\s+[^\s-][^\s]*)?\s*)+$/.test(rest);
}
