/**
 * Peon Extension - CESP (Coding Event Sound Pack) Integration for Pi
 *
 * Announces Pi events with customizable sound packs following the CESP standard.
 * Default pack: Warcraft Orc Peon
 *
 * Commands:
 *   /peon:status           - Show current pack and settings
 *   /peon:list             - List installed packs
 *   /peon:set              - Interactively select a sound pack
 *   /peon:settings         - Interactive settings editor
 *   /peon:volume [0-100]   - Set or adjust master volume
 *   /peon:mute             - Toggle global mute
 *   /peon:toggle [category]- Toggle sound categories
 *   /peon:test [category]  - Test a sound category
 */

import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
import { DynamicBorder } from "@mariozechner/pi-coding-agent";
import { Container, type SelectItem, SelectList, Text } from "@mariozechner/pi-tui";
import * as fs from "node:fs";
import * as path from "node:path";
import { exec, execFile } from "node:child_process";

// ============ CONFIGURATION ============
const PACKS_DIR = path.join(process.env.HOME || "~", ".config/openpeon/packs");
const CONFIG_FILE = path.join(process.env.HOME || "~", ".config/openpeon/config.json");
const DEFAULT_PACK = "peon";
const DEBOUNCE_MS = 500;
// =======================================

// Get the remote host to SSH back to (the machine the user is physically on)
function getRemoteHost(): string | null {
	const sshConn = process.env.SSH_CONNECTION || "";
	const clientIP = sshConn.split(" ")[0];
	if (clientIP && clientIP !== "::1" && clientIP !== "127.0.0.1") {
		return clientIP;
	}

	const sshClient = process.env.SSH_CLIENT || "";
	const clientIP2 = sshClient.split(" ")[0];
	if (clientIP2 && clientIP2 !== "::1" && clientIP2 !== "127.0.0.1") {
		return clientIP2;
	}

	return null;
}

// Map Mac paths to Linux paths for SSH playback
function mapPathToRemote(soundPath: string): string {
	if (soundPath.startsWith("/Users/thomasglopes/")) {
		return soundPath.replace("/Users/thomasglopes/", "/home/thomasgl/");
	}
	return soundPath;
}

// ============ SSH DETECTION ============
function isSSH(): boolean {
	if (process.env.SSH_CONNECTION || process.env.SSH_CLIENT || process.env.SSH_TTY) {
		return true;
	}
	try {
		const { execSync } = require("child_process");
		const result = execSync("pgrep -u $USER -f sshd-session 2>/dev/null", {
			encoding: "utf-8",
			timeout: 1000,
		});
		return result.trim().length > 0;
	} catch {
		return false;
	}
}

// CESP Core Categories
const CORE_CATEGORIES = [
	"session.start",
	"task.acknowledge",
	"task.complete",
	"task.error",
	"input.required",
	"resource.limit",
] as const;

type Category = typeof CORE_CATEGORIES[number];

interface Sound {
	file: string;
	label: string;
	sha256?: string;
}

interface CategoryConfig {
	sounds: Sound[];
}

interface OpenPeonManifest {
	cesp_version: string;
	name: string;
	display_name: string;
	version: string;
	categories: Record<string, CategoryConfig>;
	category_aliases?: Record<string, string>;
}

interface PeonConfig {
	activePack: string;
	volume: number;
	muted: boolean;
	enabledCategories: Record<Category, boolean>;
}

const DEFAULT_CONFIG: PeonConfig = {
	activePack: DEFAULT_PACK,
	volume: 0.7,
	muted: false,
	enabledCategories: {
		"session.start": true,
		"task.acknowledge": true,
		"task.complete": true,
		"task.error": true,
		"input.required": true,
		"resource.limit": true,
	},
};

// State
let config: PeonConfig = { ...DEFAULT_CONFIG };
let lastPlayed: number = 0;
let lastSoundPerCategory: Map<Category, string> = new Map();
let manifestCache: Map<string, OpenPeonManifest> = new Map();

// ============ CONFIG PERSISTENCE ============
function loadConfig(): PeonConfig {
	try {
		if (fs.existsSync(CONFIG_FILE)) {
			const content = fs.readFileSync(CONFIG_FILE, "utf-8");
			const saved = JSON.parse(content);
			return { ...DEFAULT_CONFIG, ...saved };
		}
	} catch {
		// Fall back to defaults
	}
	return { ...DEFAULT_CONFIG };
}

function saveConfig(): void {
	try {
		const dir = path.dirname(CONFIG_FILE);
		if (!fs.existsSync(dir)) {
			fs.mkdirSync(dir, { recursive: true });
		}
		fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
	} catch (err) {
		console.error("[peon] Failed to save config:", err);
	}
}

// ============ AUDIO PLAYBACK ============
function playSoundLocal(soundPath: string, volume: number): void {
	if (volume <= 0) return;

	const platform = process.platform;

	if (platform === "darwin") {
		const vol = volume.toFixed(2);
		exec(`nohup afplay -v ${vol} "${soundPath}" >/dev/null 2>&1 &`);
	} else if (platform === "linux") {
		execFile("pw-play", ["--volume=" + volume, soundPath], (err) => {
			if (err) {
				const paVol = Math.round(volume * 65536);
				execFile("paplay", ["--volume=" + paVol, soundPath], (err2) => {
					if (err2) {
						execFile("aplay", [soundPath]);
					}
				});
			}
		});
	}
}

function playSound(soundPath: string, volume: number): void {
	const remoteHost = getRemoteHost();

	if (remoteHost && isSSH() && process.platform === "darwin") {
		const remotePath = mapPathToRemote(soundPath);
		exec(
			`ssh -o ConnectTimeout=2 -o BatchMode=yes thomasgl@${remoteHost} "pw-play --volume=${volume} '${remotePath}' 2>/dev/null" 2>/dev/null || true`,
			{ timeout: 5000 }
		);
		return;
	}

	if (remoteHost && isSSH() && process.platform === "linux") {
		const vol = Math.round(volume * 100);
		exec(
			`ssh -o ConnectTimeout=2 -o BatchMode=yes ${remoteHost} "afplay -v ${vol / 100} '${soundPath}' 2>/dev/null" 2>/dev/null || true`,
			{ timeout: 5000 }
		);
		return;
	}

	playSoundLocal(soundPath, volume);
}

function sendNotification(title: string, message: string): void {
	const remoteHost = getRemoteHost();

	if (remoteHost && isSSH() && process.platform === "darwin") {
		exec(
			`ssh -o ConnectTimeout=2 -o BatchMode=yes thomasgl@${remoteHost} "notify-send -i ~/.pi/agent/extensions/assets/pi-logo.svg '${title}' '${message}'" 2>/dev/null || true`,
			{ timeout: 5000 }
		);
		return;
	}

	if (remoteHost && isSSH() && process.platform === "linux") {
		exec(
			`ssh -o ConnectTimeout=2 -o BatchMode=yes ${remoteHost} "osascript -e 'display notification "${message}" with title "${title}"'" 2>/dev/null || true`,
			{ timeout: 5000 }
		);
		return;
	}

	if (process.platform === "linux") {
		exec(
			`notify-send -i ~/.pi/agent/extensions/assets/pi-logo.svg '${title}' '${message}' 2>/dev/null || true`,
			{ timeout: 5000 }
		);
	} else if (process.platform === "darwin") {
		exec(
			`osascript -e 'display notification "${message}" with title "${title}"' 2>/dev/null || true`,
			{ timeout: 5000 }
		);
	}
}

// ============ PACK MANAGEMENT ============
function getPackPath(packName: string): string {
	return path.join(PACKS_DIR, packName);
}

function loadManifest(packName: string): OpenPeonManifest | null {
	if (manifestCache.has(packName)) {
		return manifestCache.get(packName)!;
	}

	const manifestPath = path.join(getPackPath(packName), "openpeon.json");
	if (!fs.existsSync(manifestPath)) {
		return null;
	}

	try {
		const content = fs.readFileSync(manifestPath, "utf-8");
		const manifest = JSON.parse(content) as OpenPeonManifest;
		manifestCache.set(packName, manifest);
		return manifest;
	} catch {
		return null;
	}
}

function getInstalledPacks(): string[] {
	if (!fs.existsSync(PACKS_DIR)) {
		return [];
	}

	return fs.readdirSync(PACKS_DIR).filter((name) => {
		const packPath = path.join(PACKS_DIR, name);
		const manifestPath = path.join(packPath, "openpeon.json");
		return fs.statSync(packPath).isDirectory() && fs.existsSync(manifestPath);
	});
}

function getPackDisplayName(packName: string): string {
	const manifest = loadManifest(packName);
	return manifest?.display_name || packName;
}

function resolveCategory(manifest: OpenPeonManifest, category: Category): CategoryConfig | null {
	if (manifest.categories[category]) {
		return manifest.categories[category];
	}

	if (manifest.category_aliases) {
		const aliased = manifest.category_aliases[category];
		if (aliased && manifest.categories[aliased]) {
			return manifest.categories[aliased];
		}
	}

	return null;
}

function pickSound(categoryConfig: CategoryConfig, category: Category): Sound | null {
	const sounds = categoryConfig.sounds;
	if (!sounds || sounds.length === 0) return null;

	const lastSound = lastSoundPerCategory.get(category);
	let candidates = sounds;
	if (lastSound && sounds.length > 1) {
		candidates = sounds.filter((s) => s.file !== lastSound);
	}

	const sound = candidates[Math.floor(Math.random() * candidates.length)];
	lastSoundPerCategory.set(category, sound.file);
	return sound;
}

// ============ SOUND PLAYBACK ============
function play(category: Category): void {
	if (config.muted) return;
	if (!config.enabledCategories[category]) return;

	const now = Date.now();
	if (now - lastPlayed < DEBOUNCE_MS) {
		return;
	}
	lastPlayed = now;

	const manifest = loadManifest(config.activePack);
	if (!manifest) {
		console.error(`[peon] Pack not found: ${config.activePack}`);
		return;
	}

	const categoryConfig = resolveCategory(manifest, category);
	if (!categoryConfig) {
		return;
	}

	const sound = pickSound(categoryConfig, category);
	if (!sound) return;

	const soundPath = path.join(getPackPath(config.activePack), sound.file);
	if (!fs.existsSync(soundPath)) {
		if (!sound.file.includes("/")) {
			const altPath = path.join(getPackPath(config.activePack), "sounds", sound.file);
			if (fs.existsSync(altPath)) {
				playSound(altPath, config.volume);
				return;
			}
		}
		console.error(`[peon] Sound file not found: ${soundPath}`);
		return;
	}

	playSound(soundPath, config.volume);

	const notificationMessages: Record<Category, { title: string; message: string } | null> = {
		"session.start": null,
		"task.acknowledge": null,
		"task.complete": { title: "Pi", message: "Task complete" },
		"task.error": { title: "Pi", message: "Task failed" },
		"input.required": { title: "Pi", message: "Input required" },
		"resource.limit": { title: "Pi", message: "Rate limited" },
	};

	const notification = notificationMessages[category];
	if (notification) {
		sendNotification(notification.title, notification.message);
	}
}

// ============ COMMANDS ============

function registerCommands(pi: ExtensionAPI) {
	// /peon:status - Show current pack and settings
	pi.registerCommand("peon:status", {
		description: "Show current sound pack and settings",
		handler: async (_args: string, ctx: ExtensionCommandContext) => {
			const manifest = loadManifest(config.activePack);
			const packDisplay = manifest?.display_name || config.activePack;
			const lines = [
				`Active pack: ${packDisplay}`,
				`Volume: ${Math.round(config.volume * 100)}%`,
				`Muted: ${config.muted ? "yes" : "no"}`,
				"",
				"Enabled categories:",
			];
			for (const cat of CORE_CATEGORIES) {
				const enabled = config.enabledCategories[cat] ? "✓" : "✗";
				lines.push(`  ${enabled} ${cat}`);
			}
			ctx.ui.notify(lines.join("\n"), "info");
		},
	});

	// /peon:list - List installed packs
	pi.registerCommand("peon:list", {
		description: "List installed sound packs",
		handler: async (_args: string, ctx: ExtensionCommandContext) => {
			const packs = getInstalledPacks();
			if (packs.length === 0) {
				ctx.ui.notify("No packs installed. Add packs to ~/.config/openpeon/packs/", "warning");
			} else {
				const lines = ["Installed packs:"];
				for (const pack of packs) {
					const m = loadManifest(pack);
					const marker = pack === config.activePack ? "→ " : "  ";
					lines.push(`${marker}${m?.display_name || pack} (${pack})`);
				}
				ctx.ui.notify(lines.join("\n"), "info");
			}
		},
	});

	// /peon:set - Interactively select a sound pack
	pi.registerCommand("peon:set", {
		description: "Select a sound pack interactively",
		handler: async (args: string, ctx: ExtensionCommandContext) => {
			await handleSetCommand(args.trim(), ctx);
		},
	});

	// /peon:settings - Interactive settings editor
	pi.registerCommand("peon:settings", {
		description: "Edit peon settings interactively",
		handler: async (_args: string, ctx: ExtensionCommandContext) => {
			await showSettingsMenu(ctx);
		},
	});

	// /peon:volume - Set or adjust volume
	pi.registerCommand("peon:volume", {
		description: "Set master volume (0-100) or adjust interactively",
		handler: async (args: string, ctx: ExtensionCommandContext) => {
			await handleVolumeCommand(args.trim(), ctx);
		},
	});

	// /peon:mute - Toggle mute
	pi.registerCommand("peon:mute", {
		description: "Toggle global mute",
		handler: async (_args: string, ctx: ExtensionCommandContext) => {
			config.muted = !config.muted;
			saveConfig();
			ctx.ui.notify(config.muted ? "Muted" : "Unmuted", "info");
		},
	});

	// /peon:toggle - Toggle sound categories
	pi.registerCommand("peon:toggle", {
		description: "Toggle sound categories on/off",
		handler: async (args: string, ctx: ExtensionCommandContext) => {
			await handleToggleCommand(args.trim(), ctx);
		},
	});

	// /peon:test - Test sound categories
	pi.registerCommand("peon:test", {
		description: "Test a sound category",
		handler: async (args: string, ctx: ExtensionCommandContext) => {
			await handleTestCommand(args.trim(), ctx);
		},
	});
}

// ============ COMMAND HANDLERS ============

async function handleSetCommand(packName: string, ctx: ExtensionCommandContext) {
	const packs = getInstalledPacks();

	if (packs.length === 0) {
		ctx.ui.notify("No packs installed. Add packs to ~/.config/openpeon/packs/", "warning");
		return;
	}

	// If pack name provided directly, use it
	if (packName) {
		if (!packs.includes(packName)) {
			ctx.ui.notify(`Pack '${packName}' not found.`, "error");
			return;
		}
		config.activePack = packName;
		manifestCache.delete(packName);
		saveConfig();
		ctx.ui.notify(`Switched to pack: ${getPackDisplayName(packName)}`, "info");
		return;
	}

	// Interactive selection using SelectList
	const items: SelectItem[] = packs.map((pack) => ({
		value: pack,
		label: getPackDisplayName(pack),
		description: pack === config.activePack ? "currently active" : undefined,
	}));

	const result = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
		const container = new Container();

		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
		container.addChild(new Text(theme.fg("accent", theme.bold("Select Sound Pack")), 1, 0));

		const selectList = new SelectList(items, Math.min(items.length, 10), {
			selectedPrefix: (t) => theme.fg("accent", t),
			selectedText: (t) => theme.fg("accent", t),
			description: (t) => theme.fg("muted", t),
			scrollInfo: (t) => theme.fg("dim", t),
			noMatch: (t) => theme.fg("warning", t),
		});
		selectList.onSelect = (item) => done(item.value);
		selectList.onCancel = () => done(null);
		container.addChild(selectList);

		container.addChild(new Text(theme.fg("dim", "↑↓ navigate • enter select • esc cancel"), 1, 0));
		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));

		return {
			render: (w) => container.render(w),
			invalidate: () => container.invalidate(),
			handleInput: (data) => {
				selectList.handleInput(data);
				tui.requestRender();
			},
		};
	});

	if (!result) return;

	config.activePack = result;
	manifestCache.delete(result);
	saveConfig();
	ctx.ui.notify(`Switched to pack: ${getPackDisplayName(result)}`, "info");
}

async function handleVolumeCommand(volStr: string, ctx: ExtensionCommandContext) {
	if (!volStr) {
		// Interactive volume selection
		const volumes = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
		const currentVol = Math.round(config.volume * 100);

		const items: SelectItem[] = volumes.map((v) => ({
			value: String(v),
			label: `${v}%`,
			description: v === currentVol ? "current" : undefined,
		}));

		const result = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
			const container = new Container();

			container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
			container.addChild(new Text(theme.fg("accent", theme.bold("Select Volume")), 1, 0));

			const selectList = new SelectList(items, Math.min(items.length, 10), {
				selectedPrefix: (t) => theme.fg("accent", t),
				selectedText: (t) => theme.fg("accent", t),
				description: (t) => theme.fg("muted", t),
				scrollInfo: (t) => theme.fg("dim", t),
				noMatch: (t) => theme.fg("warning", t),
			});
			selectList.onSelect = (item) => done(item.value);
			selectList.onCancel = () => done(null);
			container.addChild(selectList);

			container.addChild(new Text(theme.fg("dim", "↑↓ navigate • enter select • esc cancel"), 1, 0));
			container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));

			return {
				render: (w) => container.render(w),
				invalidate: () => container.invalidate(),
				handleInput: (data) => {
					selectList.handleInput(data);
					tui.requestRender();
				},
			};
		});

		if (!result) return;

		const vol = parseInt(result, 10);
		config.volume = vol / 100;
		saveConfig();
		ctx.ui.notify(`Volume set to ${vol}%`, "info");
		return;
	}

	const vol = parseInt(volStr, 10);
	if (isNaN(vol) || vol < 0 || vol > 100) {
		ctx.ui.notify("Volume must be 0-100", "error");
		return;
	}

	config.volume = vol / 100;
	saveConfig();
	ctx.ui.notify(`Volume set to ${vol}%`, "info");
}

async function showSettingsMenu(ctx: ExtensionCommandContext) {
	type SettingsAction = "volume" | "mute" | "pack" | "categories" | "test" | "done";

	const result = await ctx.ui.custom<SettingsAction | null>((tui, theme, _kb, done) => {
		const items: SelectItem[] = [
			{ value: "volume", label: "Volume", description: `${Math.round(config.volume * 100)}%` },
			{ value: "mute", label: "Muted", description: config.muted ? "yes" : "no" },
			{ value: "pack", label: "Active Pack", description: getPackDisplayName(config.activePack) },
			{ value: "categories", label: "Toggle Categories..." },
			{ value: "test", label: "Test Sounds..." },
			{ value: "done", label: "Done" },
		];

		const container = new Container();

		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
		container.addChild(new Text(theme.fg("accent", theme.bold("Peon Settings")), 1, 0));

		const selectList = new SelectList(items, Math.min(items.length, 10), {
			selectedPrefix: (t) => theme.fg("accent", t),
			selectedText: (t) => theme.fg("accent", t),
			description: (t) => theme.fg("muted", t),
			scrollInfo: (t) => theme.fg("dim", t),
			noMatch: (t) => theme.fg("warning", t),
		});
		selectList.onSelect = (item) => done(item.value as SettingsAction);
		selectList.onCancel = () => done(null);
		container.addChild(selectList);

		container.addChild(new Text(theme.fg("dim", "↑↓ navigate • enter select • esc cancel"), 1, 0));
		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));

		return {
			render: (w) => container.render(w),
			invalidate: () => container.invalidate(),
			handleInput: (data) => {
				selectList.handleInput(data);
				tui.requestRender();
			},
		};
	});

	if (!result || result === "done") {
		if (result === "done") {
			ctx.ui.notify("Settings saved", "info");
		}
		return;
	}

	// Handle submenu
	switch (result) {
		case "volume":
			await handleVolumeCommand("", ctx);
			break;
		case "mute":
			config.muted = !config.muted;
			saveConfig();
			ctx.ui.notify(config.muted ? "Muted" : "Unmuted", "info");
			break;
		case "pack":
			await handleSetCommand("", ctx);
			break;
		case "categories":
			await showCategoryToggle(ctx);
			break;
		case "test":
			await handleTestCommand("", ctx);
			break;
	}

	// Return to settings menu
	await showSettingsMenu(ctx);
}

async function showCategoryToggle(ctx: ExtensionCommandContext) {
	const result = await ctx.ui.custom<Category | "done" | null>((tui, theme, _kb, done) => {
		const items: SelectItem[] = [
			...CORE_CATEGORIES.map((cat) => ({
				value: cat,
				label: `${config.enabledCategories[cat] ? "✓" : "✗"} ${cat}`,
			})),
			{ value: "done", label: "← Back" },
		];

		const container = new Container();

		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
		container.addChild(new Text(theme.fg("accent", theme.bold("Toggle Categories (Space to toggle)")), 1, 0));

		const selectList = new SelectList(items, Math.min(items.length, 10), {
			selectedPrefix: (t) => theme.fg("accent", t),
			selectedText: (t) => theme.fg("accent", t),
			description: (t) => theme.fg("muted", t),
			scrollInfo: (t) => theme.fg("dim", t),
			noMatch: (t) => theme.fg("warning", t),
		});

		// Handle selection to toggle
		selectList.onSelect = (item) => {
			if (item.value === "done") {
				done("done");
				return;
			}
			// Toggle the category
			const cat = item.value as Category;
			config.enabledCategories[cat] = !config.enabledCategories[cat];
			saveConfig();
			// Update the label
			item.label = `${config.enabledCategories[cat] ? "✓" : "✗"} ${cat}`;
			tui.requestRender();
		};

		selectList.onCancel = () => done(null);
		container.addChild(selectList);

		container.addChild(new Text(theme.fg("dim", "↑↓ navigate • enter toggle • esc back"), 1, 0));
		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));

		return {
			render: (w) => container.render(w),
			invalidate: () => container.invalidate(),
			handleInput: (data) => {
				selectList.handleInput(data);
				tui.requestRender();
			},
		};
	});

	if (result === null) return;
	if (result === "done") return;

	// If a category was selected, stay in the toggle menu
	await showCategoryToggle(ctx);
}

async function handleToggleCommand(cat: string, ctx: ExtensionCommandContext) {
	if (!cat) {
		await showCategoryToggle(ctx);
		return;
	}

	if (!CORE_CATEGORIES.includes(cat as Category)) {
		ctx.ui.notify(`Unknown category: ${cat}\nAvailable: ${CORE_CATEGORIES.join(", ")}`, "error");
		return;
	}

	config.enabledCategories[cat as Category] = !config.enabledCategories[cat as Category];
	saveConfig();
	ctx.ui.notify(`${cat}: ${config.enabledCategories[cat as Category] ? "enabled" : "disabled"}`, "info");
}

async function handleTestCommand(cat: string, ctx: ExtensionCommandContext) {
	if (!cat) {
		// Interactive test selection - stays open after testing
		await showTestMenu(ctx);
		return;
	}

	if (!CORE_CATEGORIES.includes(cat as Category)) {
		ctx.ui.notify(`Unknown category: ${cat}\nAvailable: ${CORE_CATEGORIES.join(", ")}`, "error");
		return;
	}

	ctx.ui.notify(`Testing ${cat}...`, "info");
	play(cat as Category);
}

async function showTestMenu(ctx: ExtensionCommandContext) {
	const result = await ctx.ui.custom<Category | "done" | null>((tui, theme, _kb, done) => {
		const items: SelectItem[] = [
			...CORE_CATEGORIES.map((category) => ({
				value: category,
				label: category,
			})),
			{ value: "done", label: "← Back" },
		];

		const container = new Container();

		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
		container.addChild(new Text(theme.fg("accent", theme.bold("Test Sound Category")), 1, 0));

		const selectList = new SelectList(items, Math.min(items.length, 10), {
			selectedPrefix: (t) => theme.fg("accent", t),
			selectedText: (t) => theme.fg("accent", t),
			description: (t) => theme.fg("muted", t),
			scrollInfo: (t) => theme.fg("dim", t),
			noMatch: (t) => theme.fg("warning", t),
		});

		// Play sound but stay open
		selectList.onSelect = (item) => {
			if (item.value === "done") {
				done("done");
				return;
			}
			// Play the sound but don't close
			const category = item.value as Category;
			ctx.ui.notify(`Testing ${category}...`, "info");
			play(category);
		};

		selectList.onCancel = () => done(null);
		container.addChild(selectList);

		container.addChild(new Text(theme.fg("dim", "↑↓ navigate • enter test • esc back"), 1, 0));
		container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));

		return {
			render: (w) => container.render(w),
			invalidate: () => container.invalidate(),
			handleInput: (data) => {
				selectList.handleInput(data);
				tui.requestRender();
			},
		};
	});

	if (result === null) return;
	if (result === "done") return;

	// If a category was tested, stay in the test menu
	await showTestMenu(ctx);
}

// ============ EVENT WIRING ============

const INTERACTIVE_TOOLS = new Set(["question", "questionnaire"]);

export default function (pi: ExtensionAPI) {
	registerCommands(pi);

	pi.on("session_start", async (_event, ctx) => {
		config = loadConfig();
		if (!ctx.hasUI) return;
		play("session.start");
	});

	pi.on("agent_start", async (_event, ctx) => {
		if (!ctx.hasUI) return;
		play("task.acknowledge");
	});

	pi.on("agent_end", async (_event, ctx) => {
		if (!ctx.hasUI) return;
		play("task.complete");
	});

	pi.on("tool_result", async (event, ctx) => {
		if (!ctx.hasUI) return;
		if (event.isError) {
			play("task.error");
		}
	});

	pi.on("tool_call", async (event, ctx) => {
		if (!ctx.hasUI) return;
		if (INTERACTIVE_TOOLS.has(event.toolName)) {
			play("input.required");
		}
	});

	pi.on("tool_result", async (event, ctx) => {
		if (!ctx.hasUI) return;
		const firstContent = event.content?.[0];
		const content = firstContent?.type === "text" ? firstContent.text : "";
		if (event.isError && /rate.limit|quota|too.many.requests|429/i.test(content)) {
			play("resource.limit");
		}
	});

	pi.events.on("peon:input_required", (_data) => {
		play("input.required");
	});
}
