/**
 * Remote Steering - full-session surface for remote mode changes
 *
 * Purpose: allow remote questions to steer session mode (workMode,
 * runControl, permissionProfile, modelMode) in addition to answering
 * questions. This makes remote channels a first-class steering surface,
 * not just a question delivery mechanism.
 *
 * Consumer: remote-questions manager when parsing answers, and
 * auto-dispatch when checking for remote steering directives.
 */

import { getAutoSession } from "./auto/session.js";
import {
	buildModeState,
	MODEL_MODES,
	PERMISSION_PROFILES,
	RUN_CONTROL_MODES,
	WORK_MODES,
} from "./operating-model.js";
import { logWarning } from "./workflow-logger.js";

/**
 * Parse a remote answer for steering directives.
 * Looks for patterns like:
 *   /mode build
 *   /control autonomous
 *   /permission-profile trusted
 *   /model-mode deep
 *
 * @param {object} answer - the parsed remote answer object
 * @returns {{ steering: boolean, directives: Array<{cmd: string, value: string}> }}
 */
export function parseRemoteSteeringDirectives(answer) {
	if (!answer) {
		return { steering: false, directives: [] };
	}

	const text = extractAnswerText(answer);
	if (!text) {
		return { steering: false, directives: [] };
	}

	const directives = [];
	const patterns = [
		{
			regex: /\/(?:mode|work-mode)\s+([\w-]+)/g,
			cmd: "mode",
			valid: WORK_MODES,
		},
		{
			regex: /\/(?:control|run-control)\s+([\w-]+)/g,
			cmd: "control",
			valid: RUN_CONTROL_MODES,
		},
		{
			regex: /\/permission-profile\s+([\w-]+)/g,
			cmd: "permission-profile",
			valid: PERMISSION_PROFILES,
		},
		{
			regex: /\/model-mode\s+([\w-]+)/g,
			cmd: "model-mode",
			valid: MODEL_MODES,
		},
	];

	for (const { regex, cmd, valid } of patterns) {
		let match;
		while ((match = regex.exec(text)) !== null) {
			const value = match[1].toLowerCase();
			if (valid.includes(value)) {
				directives.push({ cmd, value });
			}
		}
	}

	return {
		steering: directives.length > 0,
		directives,
	};
}

function extractAnswerText(value) {
	const parts = [];
	const seen = new WeakSet();
	function visit(node) {
		if (typeof node === "string") {
			parts.push(node);
			return;
		}
		if (Array.isArray(node)) {
			for (const item of node) visit(item);
			return;
		}
		if (!node || typeof node !== "object") return;
		if (seen.has(node)) return;
		seen.add(node);
		const preferredKeys = ["text", "selected", "notes", "user_note", "answers"];
		const visitedKeys = new Set();
		for (const key of preferredKeys) {
			if (node[key] !== undefined) {
				visitedKeys.add(key);
				visit(node[key]);
			}
		}
		for (const [key, child] of Object.entries(node)) {
			if (!visitedKeys.has(key)) visit(child);
		}
	}
	visit(value);
	return parts.join(" ");
}

/** Throttle map to prevent rapid steering changes. */
const _steeringThrottle = new Map();
const STEERING_COOLDOWN_MS = 5000;
const THROTTLE_TTL_MS = 3_600_000; // 1 hour

function isThrottled(source) {
	const last = _steeringThrottle.get(source);
	if (!last) return false;
	return Date.now() - last < STEERING_COOLDOWN_MS;
}

function recordThrottle(source) {
	_steeringThrottle.set(source, Date.now());
	// Cleanup old entries to prevent unbounded growth
	const cutoff = Date.now() - THROTTLE_TTL_MS;
	for (const [key, time] of _steeringThrottle) {
		if (time < cutoff) _steeringThrottle.delete(key);
	}
}

/**
 * Apply steering directives to the current session.
 *
 * @param {Array<{cmd: string, value: string}>} directives
 * @returns {Array<{cmd: string, value: string, applied: boolean, error?: string}>}
 */
export function applyRemoteSteeringDirectives(directives, source = "default") {
	if (isThrottled(source)) {
		logWarning("remote-steering", `Steering throttled for source: ${source}`);
		return directives.map((d) => ({
			...d,
			applied: false,
			error: `Throttled: max 1 steering change per ${STEERING_COOLDOWN_MS / 1000}s`,
		}));
	}

	const session = getAutoSession();
	const results = [];
	let anyApplied = false;

	for (const { cmd, value } of directives) {
		try {
			switch (cmd) {
				case "mode":
					session.setMode({ workMode: value, reason: "remote-steering" });
					break;
				case "control":
					session.setMode({ runControl: value, reason: "remote-steering" });
					break;
				case "permission-profile":
					session.setMode({
						permissionProfile: value,
						reason: "remote-steering",
					});
					break;
				case "model-mode":
					session.setMode({ modelMode: value, reason: "remote-steering" });
					break;
				default:
					results.push({
						cmd,
						value,
						applied: false,
						error: "unknown command",
					});
					continue;
			}
			results.push({ cmd, value, applied: true });
			anyApplied = true;
		} catch (err) {
			results.push({
				cmd,
				value,
				applied: false,
				error: err.message,
			});
		}
	}

	if (anyApplied) {
		recordThrottle(source);
	}

	return results;
}

/**
 * Format steering results for remote display.
 *
 * @param {Array} results - from applyRemoteSteeringDirectives
 * @returns {string}
 */
export function formatRemoteSteeringResults(results) {
	const lines = ["SF Mode Steering"];
	for (const r of results) {
		const marker = r.applied ? "[ok]" : "[blocked]";
		lines.push(`  ${marker} /${r.cmd} ${r.value}`);
		if (r.error) lines.push(`    Error: ${r.error}`);
	}
	let mode = buildModeState();
	try {
		mode = getAutoSession().getMode();
	} catch {
		// Formatting still has value in tests and detached remote contexts.
	}
	lines.push(
		`\nCurrent: ${mode.workMode} | ${mode.runControl} | ${mode.permissionProfile} | ${mode.modelMode}`,
	);
	return lines.join("\n");
}
