import { a as createSubsystemLogger, dp as registerInternalHook, xd as openBoundaryFile } from "./env-Dlz8aQHP.js";
import "./agent-paths-DUHGyU1s.js";
import "./github-copilot-token-A9DiaCB0.js";
import { n as resolveFunctionModuleExport } from "./module-loader-DIy61p0U.js";
import "./frontmatter-DTw-J9er.js";
import { n as resolveHookConfig, r as shouldIncludeHook } from "./config-DfviqDy5.js";
import { t as loadWorkspaceHookEntries } from "./workspace-r16eQphT.js";
import { pathToFileURL } from "node:url";
import path from "node:path";
import fs from "node:fs";

//#region src/hooks/import-url.ts
/**
* Build an import URL for a hook handler module.
*
* Bundled hooks (shipped in dist/) are immutable between installs, so they
* can be imported without a cache-busting suffix — letting V8 reuse its
* module cache across gateway restarts.
*
* Workspace, managed, and plugin hooks may be edited by the user between
* restarts. For those we append `?t=<mtime>&s=<size>` so the module key
* reflects on-disk changes while staying stable for unchanged files.
*/
/**
* Sources whose handler files never change between `npm install` runs.
* Imports from these sources skip cache busting entirely.
*/
const IMMUTABLE_SOURCES = new Set(["fased-bundled"]);
function buildImportUrl(handlerPath, source) {
	const base = pathToFileURL(handlerPath).href;
	if (IMMUTABLE_SOURCES.has(source)) return base;
	try {
		const { mtimeMs, size } = fs.statSync(handlerPath);
		return `${base}?t=${mtimeMs}&s=${size}`;
	} catch {
		return `${base}?t=${Date.now()}`;
	}
}

//#endregion
//#region src/hooks/loader.ts
/**
* Dynamic loader for hook handlers
*
* Loads hook handlers from external modules based on configuration
* and from directory-based discovery (bundled, managed, workspace)
*/
const log = createSubsystemLogger("hooks:loader");
/**
* Load and register all hook handlers
*
* Loads hooks from both:
* 1. Directory-based discovery (bundled, managed, workspace)
* 2. Legacy config handlers (backwards compatibility)
*
* @param cfg - FasedAgent configuration
* @param workspaceDir - Workspace directory for hook discovery
* @returns Number of handlers successfully loaded
*
* @example
* ```ts
* const config = await loadConfig();
* const workspaceDir = resolveAgentWorkspaceDir(config, agentId);
* const count = await loadInternalHooks(config, workspaceDir);
* console.log(`Loaded ${count} hook handlers`);
* ```
*/
async function loadInternalHooks(cfg, workspaceDir, opts) {
	if (!cfg.hooks?.internal?.enabled) return 0;
	let loadedCount = 0;
	try {
		const eligible = loadWorkspaceHookEntries(workspaceDir, {
			config: cfg,
			managedHooksDir: opts?.managedHooksDir,
			bundledHooksDir: opts?.bundledHooksDir
		}).filter((entry) => shouldIncludeHook({
			entry,
			config: cfg
		}));
		for (const entry of eligible) {
			if (resolveHookConfig(cfg, entry.hook.name)?.enabled === false) continue;
			try {
				const hookBaseDir = safeRealpathOrResolve(entry.hook.baseDir);
				const opened = await openBoundaryFile({
					absolutePath: entry.hook.handlerPath,
					rootPath: hookBaseDir,
					boundaryLabel: "hook directory"
				});
				if (!opened.ok) {
					log.error(`Hook '${entry.hook.name}' handler path fails boundary checks: ${entry.hook.handlerPath}`);
					continue;
				}
				const safeHandlerPath = opened.path;
				fs.closeSync(opened.fd);
				const mod = await import(buildImportUrl(safeHandlerPath, entry.hook.source));
				const exportName = entry.metadata?.export ?? "default";
				const handler = resolveFunctionModuleExport({
					mod,
					exportName
				});
				if (!handler) {
					log.error(`Handler '${exportName}' from ${entry.hook.name} is not a function`);
					continue;
				}
				const events = entry.metadata?.events ?? [];
				if (events.length === 0) {
					log.warn(`Hook '${entry.hook.name}' has no events defined in metadata`);
					continue;
				}
				for (const event of events) registerInternalHook(event, handler);
				log.info(`Registered hook: ${entry.hook.name} -> ${events.join(", ")}${exportName !== "default" ? ` (export: ${exportName})` : ""}`);
				loadedCount++;
			} catch (err) {
				log.error(`Failed to load hook ${entry.hook.name}: ${err instanceof Error ? err.message : String(err)}`);
			}
		}
	} catch (err) {
		log.error(`Failed to load directory-based hooks: ${err instanceof Error ? err.message : String(err)}`);
	}
	const handlers = cfg.hooks.internal.handlers ?? [];
	for (const handlerConfig of handlers) try {
		const rawModule = handlerConfig.module.trim();
		if (!rawModule) {
			log.error("Handler module path is empty");
			continue;
		}
		if (path.isAbsolute(rawModule)) {
			log.error(`Handler module path must be workspace-relative (got absolute path): ${rawModule}`);
			continue;
		}
		const baseDir = path.resolve(workspaceDir);
		const modulePath = path.resolve(baseDir, rawModule);
		const baseDirReal = safeRealpathOrResolve(baseDir);
		const modulePathSafe = safeRealpathOrResolve(modulePath);
		const rel = path.relative(baseDir, modulePath);
		if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) {
			log.error(`Handler module path must stay within workspaceDir: ${rawModule}`);
			continue;
		}
		const opened = await openBoundaryFile({
			absolutePath: modulePathSafe,
			rootPath: baseDirReal,
			boundaryLabel: "workspace directory"
		});
		if (!opened.ok) {
			log.error(`Handler module path fails boundary checks under workspaceDir: ${rawModule}`);
			continue;
		}
		const safeModulePath = opened.path;
		fs.closeSync(opened.fd);
		const mod = await import(buildImportUrl(safeModulePath, "fased-workspace"));
		const exportName = handlerConfig.export ?? "default";
		const handler = resolveFunctionModuleExport({
			mod,
			exportName
		});
		if (!handler) {
			log.error(`Handler '${exportName}' from ${modulePath} is not a function`);
			continue;
		}
		registerInternalHook(handlerConfig.event, handler);
		log.info(`Registered hook (legacy): ${handlerConfig.event} -> ${modulePath}${exportName !== "default" ? `#${exportName}` : ""}`);
		loadedCount++;
	} catch (err) {
		log.error(`Failed to load hook handler from ${handlerConfig.module}: ${err instanceof Error ? err.message : String(err)}`);
	}
	return loadedCount;
}
function safeRealpathOrResolve(value) {
	try {
		return fs.realpathSync(value);
	} catch {
		return path.resolve(value);
	}
}

//#endregion
export { loadInternalHooks };