/**
 * Shared types and interfaces for the interactive shell extension.
 */

export interface InteractiveShellResult {
	exitCode: number | null;
	signal?: number;
	backgrounded: boolean;
	backgroundId?: string;
	cancelled: boolean;
	timedOut?: boolean;
	sessionId?: string;
	userTookOver?: boolean;
	/** When user triggers "Transfer" action, this contains the captured output */
	transferred?: {
		lines: string[];
		totalLines: number;
		truncated: boolean;
	};
	/** Captured before PTY disposal for dispatch mode completion notifications */
	completionOutput?: {
		lines: string[];
		totalLines: number;
		truncated: boolean;
	};
	handoffPreview?: {
		type: "tail";
		when: "exit" | "detach" | "kill" | "timeout" | "transfer";
		lines: string[];
	};
	handoff?: {
		type: "snapshot";
		when: "exit" | "detach" | "kill" | "timeout" | "transfer";
		transcriptPath: string;
		linesWritten: number;
	};
}

export interface HandsFreeUpdate {
	status: "running" | "user-takeover" | "exited" | "killed" | "agent-resumed";
	sessionId: string;
	runtime: number;
	tail: string[];
	tailTruncated: boolean;
	userTookOver?: boolean;
	// Budget tracking
	totalCharsSent?: number;
	budgetExhausted?: boolean;
}

export type MonitorStrategy = "stream" | "poll-diff" | "file-watch";

export type MonitorThresholdOperator = "lt" | "lte" | "gt" | "gte";

export interface MonitorThresholdConfig {
	captureGroup: number;
	op: MonitorThresholdOperator;
	value: number;
}

export interface MonitorTriggerConfig {
	id: string;
	literal?: string;
	regex?: string;
	cooldownMs?: number;
	threshold?: MonitorThresholdConfig;
}

export interface MonitorFileWatchConfig {
	path: string;
	recursive?: boolean;
	events?: Array<"rename" | "change">;
}

export interface MonitorConfig {
	strategy?: MonitorStrategy;
	triggers: MonitorTriggerConfig[];
	fileWatch?: MonitorFileWatchConfig;
	poll?: {
		intervalMs?: number;
	};
	persistence?: {
		stopAfterFirstEvent?: boolean;
		maxEvents?: number;
	};
	throttle?: {
		dedupeExactLine?: boolean;
		cooldownMs?: number;
	};
	detector?: {
		detectorCommand: string;
		timeoutMs?: number;
	};
}

export interface MonitorEventPayload {
	sessionId: string;
	eventId: number;
	timestamp: string;
	strategy: MonitorStrategy;
	triggerId: string;
	eventType: string;
	matchedText: string;
	lineOrDiff: string;
	stream: "pty";
}

export type MonitorTerminalReason = "stream-ended" | "script-failed" | "stopped" | "timed-out";

export interface MonitorSessionState {
	sessionId: string;
	strategy: MonitorStrategy;
	triggerIds: string[];
	status: "running" | "stopped";
	eventCount: number;
	startedAt: string;
	lastEventId?: number;
	lastEventAt?: string;
	lastTriggerId?: string;
	endedAt?: string;
	terminalReason?: MonitorTerminalReason;
	exitCode?: number | null;
	signal?: number;
}

/** Options for starting or reattaching an interactive shell session. */
export interface InteractiveShellOptions {
	command: string;
	cwd?: string;
	name?: string;
	reason?: string;
	/** Original session start time in ms since epoch, preserved across background/reattach transitions. */
	startedAt?: number;
	handoffPreviewEnabled?: boolean;
	handoffPreviewLines?: number;
	handoffPreviewMaxChars?: number;
	handoffSnapshotEnabled?: boolean;
	handoffSnapshotLines?: number;
	handoffSnapshotMaxChars?: number;
	// Hands-free / dispatch / monitor mode
	mode?: "interactive" | "hands-free" | "dispatch" | "monitor";
	monitor?: MonitorConfig;
	sessionId?: string; // Pre-generated sessionId for non-blocking modes
	handsFreeUpdateMode?: "on-quiet" | "interval";
	handsFreeUpdateInterval?: number;
	handsFreeQuietThreshold?: number;
	handsFreeUpdateMaxChars?: number;
	handsFreeMaxTotalChars?: number;
	onHandsFreeUpdate?: (update: HandsFreeUpdate) => void;
	// Auto-exit when output stops (for agents that don't exit on their own)
	autoExitOnQuiet?: boolean;
	autoExitGracePeriod?: number;
	// Auto-kill timeout
	timeout?: number;
	// When true, unregister active session on completion (blocking tool call path).
	// When false/undefined, keep registered so agent can query result later.
	streamingMode?: boolean;
	// Existing PTY session (for attach flow -- skip creating a new PTY)
	existingSession?: import("./pty-session.js").PtyTerminalSession;
	onUnfocus?: () => void;
}

export type DialogChoice = "kill" | "background" | "transfer" | "cancel" | "return-to-agent";
export type OverlayState = "running" | "exited" | "detach-dialog" | "hands-free";

// UI constants
export const FOOTER_LINES_COMPACT = 2;
export const FOOTER_LINES_DIALOG = 6;
export const HEADER_LINES = 4;

/** Format milliseconds to human-readable duration */
export function formatDuration(ms: number): string {
	const seconds = Math.floor(ms / 1000);
	if (seconds < 60) return `${seconds}s`;
	const minutes = Math.floor(seconds / 60);
	if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
	const hours = Math.floor(minutes / 60);
	return `${hours}h ${minutes % 60}m`;
}

/** Format a key shortcut string for display (capitalize modifier names) */
export function formatShortcut(shortcut: string): string {
	return shortcut
		.replace(/ctrl/gi, "Ctrl")
		.replace(/shift/gi, "Shift")
		.replace(/alt/gi, "Alt");
}

/** Format milliseconds with ms precision for shorter durations */
export function formatDurationMs(ms: number): string {
	if (ms < 1000) return `${ms}ms`;
	const seconds = Math.floor(ms / 1000);
	if (seconds < 60) return `${seconds}s`;
	const minutes = Math.floor(seconds / 60);
	if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
	const hours = Math.floor(minutes / 60);
	return `${hours}h ${minutes % 60}m`;
}
