// SF2 — Extension registration: wires all SF tools, commands, and hooks into pi
import { loadEcosystemExtensions } from "../ecosystem/loader.js";
import { getErrorMessage } from "../error-utils.js";
import { registerExitCommand } from "../exit-command.js";
import { registerSiftSearchTool } from "../tools/sift-search-tool.js";
import { logWarning } from "../workflow-logger.js";
import { registerWorktreeCommand } from "../worktree-command.js";
import { registerContextBoardTool } from "./context-board-tool.js";
import { writeCrashLog } from "./crash-log.js";
import { registerDbTools } from "./db-tools.js";
import { registerDynamicTools } from "./dynamic-tools.js";
import { registerExecTools } from "./exec-tools.js";
import { registerJournalTools } from "./journal-tools.js";
import { registerJudgmentTools } from "./judgment-tools.js";
import { registerMemoryTools } from "./memory-tools.js";
import { registerProductAuditTool } from "./product-audit-tool.js";
import { registerQueryTools } from "./query-tools.js";
import { registerHooks } from "./register-hooks.js";
import { registerShortcuts } from "./register-shortcuts.js";
import { registerSessionTodoTool } from "./session-todo-tools.js";

export { writeCrashLog } from "./crash-log.js";
export function handleRecoverableExtensionProcessError(err) {
	if (err.code === "EPIPE") {
		process.exit(0);
	}
	if (err.code === "ENOENT") {
		const syscall = err.syscall;
		if (syscall?.startsWith("spawn")) {
			process.stderr.write(
				`[forge] spawn ENOENT: ${err.path ?? "unknown"} — command not found\n`,
			);
			return true;
		}
		if (syscall === "uv_cwd") {
			process.stderr.write(`[forge] ENOENT (${syscall}): ${err.message}\n`);
			return true;
		}
	}
	return false;
}
function installEpipeGuard() {
	if (
		!process
			.listeners("uncaughtException")
			.some((listener) => listener.name === "_sfEpipeGuard")
	) {
		const _sfEpipeGuard = (err) => {
			if (handleRecoverableExtensionProcessError(err)) return;
			// Write crash log and exit cleanly for unrecoverable errors.
			// Logging and continuing was the original double-fault fix (#3163), but
			// continuing in an indeterminate state is worse than a clean exit (#3348).
			writeCrashLog(err, "uncaughtException");
			process.exit(1);
		};
		process.on("uncaughtException", _sfEpipeGuard);
	}
	if (
		!process
			.listeners("unhandledRejection")
			.some((listener) => listener.name === "_sfRejectionGuard")
	) {
		const _sfRejectionGuard = (reason, _promise) => {
			const err = reason instanceof Error ? reason : new Error(String(reason));
			if (handleRecoverableExtensionProcessError(err)) return;
			writeCrashLog(err, "unhandledRejection");
			process.exit(1);
		};
		process.on("unhandledRejection", _sfRejectionGuard);
	}
}
export function registerSfExtension(pi) {
	// Note: registerSFCommand is called by index.ts before this function,
	// so we intentionally skip it here to avoid double-registration.
	registerWorktreeCommand(pi);
	registerExitCommand(pi);
	installEpipeGuard();
	pi.registerCommand("kill", {
		description: "Exit SF immediately (no cleanup)",
		handler: async (_args, _ctx) => {
			process.exit(0);
		},
	});
	const ecosystemHandlers = [];
	// Wrap non-critical registrations individually so one failure
	// doesn't prevent the others from loading.
	const nonCriticalRegistrations = [
		["dynamic-tools", () => registerDynamicTools(pi)],
		["db-tools", () => registerDbTools(pi)],
		["exec-tools", () => registerExecTools(pi)],
		["memory-tools", () => registerMemoryTools(pi)],
		["context-board-tool", () => registerContextBoardTool(pi)],
		["product-audit-tool", () => registerProductAuditTool(pi)],
		["journal-tools", () => registerJournalTools(pi)],
		["judgment-tools", () => registerJudgmentTools(pi)],
		["query-tools", () => registerQueryTools(pi)],
		["sift-search-tool", () => registerSiftSearchTool(pi)],
		["shortcuts", () => registerShortcuts(pi)],
		["session-todo-tool", () => registerSessionTodoTool(pi)],
		["hooks", () => registerHooks(pi, ecosystemHandlers)],
		[
			"ecosystem",
			() => {
				void loadEcosystemExtensions(pi, ecosystemHandlers).catch((err) => {
					logWarning(
						"bootstrap",
						`Failed to load ecosystem extensions: ${getErrorMessage(err)}`,
					);
				});
			},
		],
	];
	for (const [name, register] of nonCriticalRegistrations) {
		try {
			register();
		} catch (err) {
			logWarning(
				"bootstrap",
				`Failed to register ${name}: ${getErrorMessage(err)}`,
			);
		}
	}
}
