import { createLogger } from "../utils/logger/index.js";
import { init_logger } from "../utils/logger.js";
import { registerSpeechProvider } from "../voice/tts/speech-registry.js";
import { HOOK_EXECUTION_MODES } from "./types/hooks.js";
import { commandRegistry } from "../chat-commands/registry.js";
import { TypedEventBus } from "./typed-event-bus.js";
import { ExtensionRegistryImpl } from "./extension-registry-impl.js";
import { registerMarketplaceAdapter, unregisterMarketplaceAdapter } from "../agent/skills/marketplace/registry.js";
import { registerMediaUnderstandingProvider } from "../media-understanding/registry.js";
import { isAbsolute, resolve } from "path";
import { EventEmitter } from "events";
//#region src/extensions/api.ts
init_logger();
var ExtensionApiImpl = class {
	_tools = /* @__PURE__ */ new Map();
	_hooks = /* @__PURE__ */ new Map();
	_typedHooks = /* @__PURE__ */ new Map();
	_eventBus = new EventEmitter();
	_typedEventBus;
	_registry;
	_runtime;
	_reloadConfigPrefixes = [];
	_registeredCommandIds = [];
	_registeredSpeechProviderIds = [];
	_registeredMediaUnderstandingProviderIds = [];
	_registeredMarketplaceAdapterIds = [];
	_manifestCommands = /* @__PURE__ */ new Map();
	constructor(id, name, version, source, config, extensionConfig, _logger, _resolvePath, _coreRegistry, runtime) {
		this.id = id;
		this.name = name;
		this.version = version;
		this.source = source;
		this.config = config;
		this.extensionConfig = extensionConfig;
		this._logger = _logger;
		this._resolvePath = _resolvePath;
		this._coreRegistry = _coreRegistry;
		this._typedEventBus = new TypedEventBus({ logger: _logger });
		this._registry = _coreRegistry ?? new ExtensionRegistryImpl();
		this._runtime = runtime ?? {
			config: this.config,
			log: this._logger
		};
	}
	get logger() {
		return this._logger;
	}
	get runtime() {
		return this._runtime;
	}
	registerTool(tool) {
		if (this._tools.has(tool.name)) this._logger.warn(`Tool ${tool.name} already registered, overwriting`);
		this._tools.set(tool.name, tool);
		this._logger.info(`Registered tool: ${tool.name}`);
	}
	registerHook(event, handler, opts) {
		if (!this._hooks.has(event)) this._hooks.set(event, /* @__PURE__ */ new Set());
		this._hooks.get(event).add(handler);
		if (opts?.once) {
			const wrapper = async (...args) => {
				await handler(...args);
				this._hooks.get(event)?.delete(wrapper);
			};
			this._hooks.get(event).add(wrapper);
		}
		this._logger.info(`Registered hook: ${event}`);
	}
	/**
	*  Strongly typed hook registration
	* Provides compile-time type checking and IDE autocomplete
	* 
	* @example
	* api.on('before_agent_start', async (event, ctx) => {
	*   console.log('Agent starting with prompt:', event.prompt);
	*   return { systemPrompt: event.systemPrompt };
	* });
	*/
	onHook(hookName, handler, _opts) {
		const mode = HOOK_EXECUTION_MODES[hookName];
		if (!this._typedHooks.has(hookName)) this._typedHooks.set(hookName, /* @__PURE__ */ new Set());
		this._typedHooks.get(hookName).add(handler);
		this._logger.debug(`Registered typed hook: ${hookName} (mode: ${mode})`);
	}
	/**
	*  Get typed hooks for a specific event
	*/
	_getTypedHooks(hookName) {
		const hooks = this._typedHooks.get(hookName);
		return hooks ? Array.from(hooks) : [];
	}
	/** Adds a ChannelPlugin to the extension registry; emits `channel:register` for observability. */
	registerChannel(registration) {
		const plugin = registration.plugin;
		this._registry.addChannelPlugin(plugin);
		this._eventBus.emit("channel:register", plugin);
		this._logger.info(`Registered channel plugin: ${plugin.id}`);
	}
	registerHttpRoute(path, handler) {
		this._eventBus.emit("http:route", {
			path,
			handler
		});
		this._logger.info(`Registered HTTP route: ${path}`);
	}
	registerCommand(command) {
		const commandId = `ext.${this.id}.${command.name}`;
		const definition = {
			id: commandId,
			name: command.name,
			aliases: command.aliases,
			description: command.description,
			category: "extension",
			scope: command.scope ?? ["global"],
			acceptsArgs: command.acceptsArgs,
			examples: command.examples,
			handler: async (ctx, args) => {
				const extensionCtx = {
					sessionKey: ctx.sessionKey,
					source: ctx.source,
					isGroup: ctx.isGroup,
					config: ctx.config,
					reply: (text) => ctx.reply(text)
				};
				const result = await command.handler(args, extensionCtx);
				if (!result) return {
					content: "",
					success: true
				};
				return {
					content: result.content,
					success: result.success ?? true
				};
			}
		};
		commandRegistry.register(definition);
		this._registeredCommandIds.push(commandId);
		this._registry.addCommand(command);
		this._eventBus.emit("command:register", command);
		this._logger.info(`Registered chat command: /${command.name}`);
	}
	/** Injected from manifest `ui.contributions.commands` by the loader. */
	_setManifestCommands(commands) {
		this._manifestCommands.clear();
		for (const c of commands) this._manifestCommands.set(c.id, c);
	}
	onCommand(commandId, handler) {
		const meta = this._manifestCommands.get(commandId);
		if (!meta) {
			this._logger.warn(`onCommand: unknown command id "${commandId}" — registering runtime-only chat command`);
			const name = commandId.includes(".") ? (commandId.split(".").pop() ?? commandId).trim() : commandId;
			this.registerCommand({
				name: name || commandId,
				description: commandId,
				handler
			});
			return;
		}
		let name;
		if (meta.chatAlias?.trim()) name = meta.chatAlias.trim().replace(/^\//, "");
		else name = meta.id.includes(".") ? meta.id.split(".").pop() ?? meta.id : meta.id;
		if (!name) name = meta.id;
		this.registerCommand({
			name,
			description: meta.title,
			handler
		});
		this._logger.info(`Bound manifest command "${commandId}" as /${name}`);
	}
	registerReload(handler) {
		const prefixes = this._reloadConfigPrefixes.length > 0 ? this._reloadConfigPrefixes : [`extensions.${this.id}`];
		this._registry.addReloadRegistration({
			extensionId: this.id,
			handler,
			configPrefixes: prefixes
		});
		this._logger.info("Registered reload handler");
	}
	/** Called from loader when manifest declares `reload.configPrefixes`. */
	_setReloadConfigPrefixes(prefixes) {
		this._reloadConfigPrefixes = [...prefixes];
	}
	registerService(service) {
		this._eventBus.emit("service:register", service);
		this._logger.info(`Registered service: ${service.id}`);
	}
	registerGatewayMethod(method, handler) {
		this._eventBus.emit("gateway:method", {
			method,
			handler
		});
		this._logger.info(`Registered gateway method: ${method}`);
	}
	registerCli(factory, opts) {
		const reg = {
			extensionId: this.id,
			commands: opts?.commands ?? [],
			factory
		};
		this._registry.addCliRegistration(reg);
		this._eventBus.emit("cli:register", reg);
		this._logger.info(`Registered CLI factory${opts?.commands?.length ? ` (${opts.commands.join(", ")})` : ""}`);
	}
	resolvePath(input) {
		return this._resolvePath(input);
	}
	emit(event, data) {
		this._eventBus.emit(event, data);
	}
	on(event, handler) {
		this._eventBus.on(event, handler);
	}
	off(event, handler) {
		this._eventBus.off(event, handler);
	}
	get events() {
		return this._typedEventBus;
	}
	/**
	* Register a full ProviderPlugin
	*/
	registerProvider(plugin) {
		import("../providers/plugin-registry.js").then(({ getProviderRegistry }) => {
			getProviderRegistry().register(plugin);
			this._logger.info(`Extension registered provider: ${plugin.id}`);
		}).catch((err) => {
			this._logger.error(`Failed to register provider: ${err instanceof Error ? err.message : String(err)}`);
		});
	}
	/**
	* Register a full ProviderPlugin (alias for registerProvider)
	*/
	registerProviderPlugin(plugin) {
		this.registerProvider(plugin);
	}
	registerSpeechProvider(plugin) {
		registerSpeechProvider(plugin);
		if (!this._registeredSpeechProviderIds.includes(plugin.id)) this._registeredSpeechProviderIds.push(plugin.id);
		this._logger.info(`Registered speech provider: ${plugin.id}`);
	}
	/** Speech provider ids registered through this extension API instance. */
	getRegisteredSpeechProviderIds() {
		return [...this._registeredSpeechProviderIds];
	}
	registerMediaUnderstandingProvider(plugin) {
		registerMediaUnderstandingProvider(plugin);
		if (!this._registeredMediaUnderstandingProviderIds.includes(plugin.id)) this._registeredMediaUnderstandingProviderIds.push(plugin.id);
		this._logger.info(`Registered media understanding provider: ${plugin.id}`);
	}
	/** Media understanding provider ids registered through this extension API instance. */
	getRegisteredMediaUnderstandingProviderIds() {
		return [...this._registeredMediaUnderstandingProviderIds];
	}
	/**
	* Register a skills marketplace adapter so it appears in the UI provider picker.
	*/
	registerMarketplaceAdapter(registration) {
		registerMarketplaceAdapter(registration);
		const adapterId = registration.adapter.id;
		if (!this._registeredMarketplaceAdapterIds.includes(adapterId)) this._registeredMarketplaceAdapterIds.push(adapterId);
		this._logger.info(`Extension registered marketplace adapter: ${adapterId}`);
	}
	registerFlag(_name, _config) {}
	getFlag(_name) {}
	registerShortcut(_key, _config) {}
	registerTui(register) {
		this._registry.addTuiRegistration(this.id, register);
		this._logger.info("Registered TUI contributions (deferred until xopc tui starts)");
	}
	_getTools() {
		return this._tools;
	}
	_getHooks() {
		return this._hooks;
	}
	_getEventBus() {
		return this._eventBus;
	}
	_cleanup() {
		for (const commandId of this._registeredCommandIds) commandRegistry.unregister(commandId);
		this._registeredCommandIds = [];
		this._manifestCommands.clear();
		this._registry.removeReloadRegistration(this.id);
		for (const adapterId of this._registeredMarketplaceAdapterIds) unregisterMarketplaceAdapter(adapterId);
		this._registeredMarketplaceAdapterIds = [];
		this._typedEventBus.cleanupAll();
		this._eventBus.removeAllListeners();
		this._hooks.clear();
		this._typedHooks.clear();
	}
};
function createExtensionLogger(prefix) {
	const childLogger = createLogger(`Extension:${prefix}`);
	return {
		debug: (msg) => childLogger.debug(msg),
		info: (msg) => childLogger.info(msg),
		warn: (msg) => childLogger.warn(msg),
		error: (msg) => childLogger.error(msg)
	};
}
function createPathResolver(extensionDir, workspaceDir) {
	return (input) => {
		if (input.startsWith("~")) return input.replace("~", process.env.HOME || "");
		if (input.startsWith(".")) return resolve(extensionDir, input);
		if (!isAbsolute(input)) return resolve(workspaceDir, input);
		return input;
	};
}
//#endregion
export { ExtensionApiImpl, createExtensionLogger, createPathResolver };

//# sourceMappingURL=api.js.map