/**
 * SF Extensions Command — /extensions
 *
 * Manage the extension registry: list, enable, disable, info.
 * Self-contained — no imports outside the extensions tree (extensions are loaded
 * via jiti at runtime from ~/.sf/agent/, not compiled by tsc).
 */
import {
	existsSync,
	mkdirSync,
	readdirSync,
	readFileSync,
	renameSync,
	writeFileSync,
} from "node:fs";
import { dirname, join } from "node:path";
import { sfHome } from "./sf-home.js";

// ─── Registry I/O ───────────────────────────────────────────────────────────
/**
 * Get the path to the extension registry file.
 */
function getRegistryPath() {
	return join(sfHome(), "extensions", "registry.json");
}
/**
 * Get the path to the agent extensions directory.
 */
function getAgentExtensionsDir() {
	return join(sfHome(), "agent", "extensions");
}
/**
 * Load the extension registry, defaulting to an empty registry on error.
 */
function loadRegistry() {
	const filePath = getRegistryPath();
	try {
		if (!existsSync(filePath)) return { version: 1, entries: {} };
		const raw = readFileSync(filePath, "utf-8");
		const parsed = JSON.parse(raw);
		if (
			typeof parsed === "object" &&
			parsed !== null &&
			parsed.version === 1 &&
			typeof parsed.entries === "object"
		) {
			return parsed;
		}
		return { version: 1, entries: {} };
	} catch {
		return { version: 1, entries: {} };
	}
}
/**
 * Save the extension registry to disk (atomic via temp file).
 */
function saveRegistry(registry) {
	const filePath = getRegistryPath();
	try {
		mkdirSync(dirname(filePath), { recursive: true });
		const tmp = filePath + ".tmp";
		writeFileSync(tmp, JSON.stringify(registry, null, 2), "utf-8");
		renameSync(tmp, filePath);
	} catch {
		/* non-fatal */
	}
}
/**
 * Check if an extension is enabled in the registry (defaults to true if not registered).
 */
function isEnabled(registry, id) {
	const entry = registry.entries[id];
	if (!entry) return true;
	return entry.enabled;
}
/**
 * Load extension manifest from a directory, or null if not found/invalid.
 */
function readManifest(dir) {
	const mPath = join(dir, "extension-manifest.json");
	if (!existsSync(mPath)) return null;
	try {
		const raw = JSON.parse(readFileSync(mPath, "utf-8"));
		if (typeof raw?.id === "string" && typeof raw?.name === "string")
			return raw;
		return null;
	} catch {
		return null;
	}
}
/**
 * Discover all extension manifests from the agent extensions directory.
 */
function discoverManifests() {
	const extDir = getAgentExtensionsDir();
	const manifests = new Map();
	if (!existsSync(extDir)) return manifests;
	for (const entry of readdirSync(extDir, { withFileTypes: true })) {
		if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
		const m = readManifest(join(extDir, entry.name));
		if (m) manifests.set(m.id, m);
	}
	return manifests;
}
// ─── Command Handler ────────────────────────────────────────────────────────
/**
 * Handler for /extensions subcommands (list, enable, disable, info).
 */
export async function handleExtensions(args, ctx) {
	const parts = args.split(/\s+/).filter(Boolean);
	const subCmd = parts[0] ?? "list";
	if (subCmd === "list") {
		handleList(ctx);
		return;
	}
	if (subCmd === "enable") {
		handleEnable(parts[1], ctx);
		return;
	}
	if (subCmd === "disable") {
		handleDisable(parts[1], parts.slice(2).join(" "), ctx);
		return;
	}
	if (subCmd === "info") {
		handleInfo(parts[1], ctx);
		return;
	}
	ctx.ui.notify(
		`Unknown: /extensions ${subCmd}. Usage: /extensions [list|enable|disable|info]`,
		"warning",
	);
}
/**
 * List all discovered extensions with their status and capabilities.
 */
function handleList(ctx) {
	const manifests = discoverManifests();
	const registry = loadRegistry();
	if (manifests.size === 0) {
		ctx.ui.notify("No extension manifests found.", "warning");
		return;
	}
	// Sort: core first, then alphabetical
	const sorted = [...manifests.values()].sort((a, b) => {
		if (a.tier === "core" && b.tier !== "core") return -1;
		if (b.tier === "core" && a.tier !== "core") return 1;
		return a.id.localeCompare(b.id);
	});
	const lines = [];
	const hdr =
		padRight("Extensions", 38) +
		padRight("Status", 10) +
		padRight("Tier", 10) +
		padRight("Tools", 7) +
		"Commands";
	lines.push(hdr);
	lines.push("─".repeat(hdr.length));
	for (const m of sorted) {
		const enabled = isEnabled(registry, m.id);
		const status = enabled ? "enabled" : "disabled";
		const toolCount = m.provides?.tools?.length ?? 0;
		const cmdCount = m.provides?.commands?.length ?? 0;
		const label = `${m.id} (${m.name})`;
		lines.push(
			padRight(label, 38) +
				padRight(status, 10) +
				padRight(m.tier, 10) +
				padRight(String(toolCount), 7) +
				String(cmdCount),
		);
		if (!enabled) {
			lines.push(`  ↳ sf extensions enable ${m.id}`);
		}
	}
	ctx.ui.notify(lines.join("\n"), "info");
}
/**
 * Enable a disabled extension in the registry.
 */
function handleEnable(id, ctx) {
	if (!id) {
		ctx.ui.notify("Usage: /extensions enable <id>", "warning");
		return;
	}
	const manifests = discoverManifests();
	if (!manifests.has(id)) {
		ctx.ui.notify(
			`Extension "${id}" not found. Run /extensions list to see available extensions.`,
			"warning",
		);
		return;
	}
	const registry = loadRegistry();
	if (isEnabled(registry, id)) {
		ctx.ui.notify(`Extension "${id}" is already enabled.`, "info");
		return;
	}
	const entry = registry.entries[id];
	if (entry) {
		entry.enabled = true;
		delete entry.disabledAt;
		delete entry.disabledReason;
	} else {
		registry.entries[id] = { id, enabled: true, source: "bundled" };
	}
	saveRegistry(registry);
	ctx.ui.notify(`Enabled "${id}". Restart SF to activate.`, "info");
}
function handleDisable(id, reason, ctx) {
	if (!id) {
		ctx.ui.notify("Usage: /extensions disable <id>", "warning");
		return;
	}
	const manifests = discoverManifests();
	const manifest = manifests.get(id) ?? null;
	if (!manifests.has(id)) {
		ctx.ui.notify(
			`Extension "${id}" not found. Run /extensions list to see available extensions.`,
			"warning",
		);
		return;
	}
	if (manifest?.tier === "core") {
		ctx.ui.notify(
			`Cannot disable "${id}" — it is a core extension.`,
			"warning",
		);
		return;
	}
	const registry = loadRegistry();
	if (!isEnabled(registry, id)) {
		ctx.ui.notify(`Extension "${id}" is already disabled.`, "info");
		return;
	}
	const entry = registry.entries[id];
	if (entry) {
		entry.enabled = false;
		entry.disabledAt = new Date().toISOString();
		entry.disabledReason = reason || undefined;
	} else {
		registry.entries[id] = {
			id,
			enabled: false,
			source: "bundled",
			disabledAt: new Date().toISOString(),
			disabledReason: reason || undefined,
		};
	}
	saveRegistry(registry);
	ctx.ui.notify(`Disabled "${id}". Restart SF to deactivate.`, "info");
}
function handleInfo(id, ctx) {
	if (!id) {
		ctx.ui.notify("Usage: /extensions info <id>", "warning");
		return;
	}
	const manifests = discoverManifests();
	const manifest = manifests.get(id);
	if (!manifest) {
		ctx.ui.notify(`Extension "${id}" not found.`, "warning");
		return;
	}
	const registry = loadRegistry();
	const enabled = isEnabled(registry, id);
	const entry = registry.entries[id];
	const lines = [
		`${manifest.name} (${manifest.id})`,
		"",
		`  Version:     ${manifest.version}`,
		`  Description: ${manifest.description}`,
		`  Tier:        ${manifest.tier}`,
		`  Status:      ${enabled ? "enabled" : "disabled"}`,
	];
	if (entry?.disabledAt) {
		lines.push(`  Disabled at: ${entry.disabledAt}`);
	}
	if (entry?.disabledReason) {
		lines.push(`  Reason:      ${entry.disabledReason}`);
	}
	if (manifest.provides) {
		lines.push("");
		lines.push("  Provides:");
		if (manifest.provides.tools?.length) {
			lines.push(`    Tools:     ${manifest.provides.tools.join(", ")}`);
		}
		if (manifest.provides.commands?.length) {
			lines.push(`    Commands:  ${manifest.provides.commands.join(", ")}`);
		}
		if (manifest.provides.hooks?.length) {
			lines.push(`    Hooks:     ${manifest.provides.hooks.join(", ")}`);
		}
		if (manifest.provides.shortcuts?.length) {
			lines.push(`    Shortcuts: ${manifest.provides.shortcuts.join(", ")}`);
		}
	}
	if (manifest.dependencies) {
		lines.push("");
		lines.push("  Dependencies:");
		if (manifest.dependencies.extensions?.length) {
			lines.push(
				`    Extensions: ${manifest.dependencies.extensions.join(", ")}`,
			);
		}
		if (manifest.dependencies.runtime?.length) {
			lines.push(`    Runtime:    ${manifest.dependencies.runtime.join(", ")}`);
		}
	}
	ctx.ui.notify(lines.join("\n"), "info");
}
function padRight(str, len) {
	return str.length >= len ? str + " " : str + " ".repeat(len - str.length);
}
