import { isSurfaceFeatureGateEnabled } from '../runtime/feature-flags/index.js';
export class ChannelPluginRegistry {
    plugins = new Map();
    pluginsBySurface = new Map();
    pluginsByPath = new Map();
    featureFlags;
    version = 0;
    constructor(options = {}) {
        this.featureFlags = options.featureFlags ?? null;
    }
    isPluginEnabled(plugin) {
        return isSurfaceFeatureGateEnabled(this.featureFlags, plugin.surface);
    }
    register(plugin) {
        const existingById = this.plugins.get(plugin.id);
        if (existingById?.webhookPath) {
            this.pluginsByPath.delete(existingById.webhookPath);
        }
        if (existingById && this.pluginsBySurface.get(existingById.surface)?.id === existingById.id) {
            this.pluginsBySurface.delete(existingById.surface);
        }
        const existingBySurface = this.pluginsBySurface.get(plugin.surface);
        if (existingBySurface && existingBySurface.id !== plugin.id) {
            this.plugins.delete(existingBySurface.id);
            if (existingBySurface.webhookPath)
                this.pluginsByPath.delete(existingBySurface.webhookPath);
        }
        this.plugins.set(plugin.id, plugin);
        this.pluginsBySurface.set(plugin.surface, plugin);
        if (plugin.webhookPath) {
            this.pluginsByPath.set(plugin.webhookPath, plugin);
        }
        this.version += 1;
    }
    unregister(pluginId) {
        const plugin = this.plugins.get(pluginId);
        if (!plugin)
            return false;
        this.plugins.delete(pluginId);
        if (this.pluginsBySurface.get(plugin.surface)?.id === pluginId) {
            this.pluginsBySurface.delete(plugin.surface);
        }
        if (plugin.webhookPath && this.pluginsByPath.get(plugin.webhookPath)?.id === pluginId) {
            this.pluginsByPath.delete(plugin.webhookPath);
        }
        this.version += 1;
        return true;
    }
    getVersion() {
        return this.version;
    }
    list() {
        return [...this.plugins.values()]
            .filter((plugin) => this.isPluginEnabled(plugin))
            .sort((a, b) => a.displayName.localeCompare(b.displayName));
    }
    listDescriptors() {
        return this.list().map((plugin) => ({
            id: plugin.id,
            surface: plugin.surface,
            displayName: plugin.displayName,
            capabilities: [...plugin.capabilities],
            ...(plugin.setupVersion ? { setupVersion: plugin.setupVersion } : {}),
        }));
    }
    getBySurface(surface) {
        const plugin = this.pluginsBySurface.get(surface);
        return plugin && this.isPluginEnabled(plugin) ? plugin : null;
    }
    get(pluginId) {
        const plugin = this.plugins.get(pluginId);
        return plugin && this.isPluginEnabled(plugin) ? plugin : null;
    }
    getByPath(pathname) {
        const plugin = this.pluginsByPath.get(pathname);
        return plugin && this.isPluginEnabled(plugin) ? plugin : null;
    }
    async handleInbound(pathname, req) {
        const plugin = this.getByPath(pathname);
        if (!plugin?.handleInbound)
            return null;
        return plugin.handleInbound(req);
    }
    async deliverReply(surface, pending, message) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.deliverReply)
            return false;
        await plugin.deliverReply(pending, message);
        return true;
    }
    async deliverProgress(surface, pending, progress) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.deliverProgress)
            return false;
        await plugin.deliverProgress(pending, progress);
        return true;
    }
    async notifyApproval(surface, approval, binding) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.notifyApproval)
            return false;
        await plugin.notifyApproval(approval, binding);
        return true;
    }
    async getRenderPolicy(surface) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.renderPolicy)
            return null;
        return plugin.renderPolicy();
    }
    async render(surface, request) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return null;
        if (plugin.renderEvent) {
            const result = await plugin.renderEvent(request);
            if (result)
                return result;
            return {
                delivered: true,
                metadata: {
                    surface,
                    pluginId: plugin.id,
                    outcome: 'rendered-without-result',
                    reason: 'plugin-render-event-returned-void',
                },
            };
        }
        if (request.phase === 'final' && plugin.deliverReply) {
            await plugin.deliverReply(request.pending, request.text);
            return { delivered: true, metadata: { surface, pluginId: plugin.id, fallback: 'deliverReply' } };
        }
        if (request.phase === 'progress' && plugin.deliverProgress) {
            await plugin.deliverProgress(request.pending, request.text);
            return { delivered: true, metadata: { surface, pluginId: plugin.id, fallback: 'deliverProgress' } };
        }
        return null;
    }
    async getSetupSchema(surface, accountId) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.getSetupSchema)
            return null;
        return plugin.getSetupSchema(accountId);
    }
    async doctor(surface, accountId) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.doctor)
            return null;
        return plugin.doctor(accountId);
    }
    async listRepairActions(surface, accountId) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.listRepairActions)
            return [];
        return plugin.listRepairActions(accountId);
    }
    async getLifecycleState(surface, accountId) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.getLifecycleState)
            return null;
        return plugin.getLifecycleState(accountId);
    }
    async resolveAllowlist(surface, input) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.resolveAllowlist)
            return null;
        return plugin.resolveAllowlist(input);
    }
    async editAllowlist(surface, input) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.editAllowlist)
            return null;
        return plugin.editAllowlist(input);
    }
    async listStatus() {
        const snapshots = [];
        for (const plugin of this.list()) {
            if (!plugin.getStatus)
                continue;
            snapshots.push(await plugin.getStatus());
        }
        return snapshots.sort((a, b) => a.label.localeCompare(b.label));
    }
    async listAccounts(surface) {
        const plugins = surface ? [this.getBySurface(surface)].filter((value) => value !== null) : this.list();
        const accounts = [];
        for (const plugin of plugins) {
            if (!plugin.listAccounts)
                continue;
            accounts.push(...await plugin.listAccounts());
        }
        return accounts.sort((a, b) => a.surface.localeCompare(b.surface) || a.label.localeCompare(b.label) || a.id.localeCompare(b.id));
    }
    async getAccount(surface, accountId) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return null;
        if (plugin.getAccount) {
            return plugin.getAccount(accountId);
        }
        if (plugin.listAccounts) {
            const accounts = await plugin.listAccounts();
            return accounts.find((entry) => entry.id === accountId || entry.accountId === accountId) ?? null;
        }
        return null;
    }
    async runAccountAction(surface, action, accountId, input) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return null;
        if (plugin.runAccountAction)
            return plugin.runAccountAction(action, accountId, input);
        if (action === 'start' && plugin.startAccount)
            return plugin.startAccount(accountId, input);
        if (action === 'stop' && plugin.stopAccount)
            return plugin.stopAccount(accountId, input);
        if ((action === 'login' || action === 'connect') && plugin.loginAccount)
            return plugin.loginAccount(accountId, input);
        if (action === 'setup' && plugin.loginWithQrStart)
            return plugin.loginWithQrStart(accountId, input);
        if (action === 'wait_login' && plugin.loginWithQrWait)
            return plugin.loginWithQrWait(accountId, input);
        if ((action === 'logout' || action === 'disconnect') && plugin.logoutAccount)
            return plugin.logoutAccount(accountId, input);
        if (action === 'inspect') {
            const account = accountId ? await this.getAccount(surface, accountId) : (await this.listAccounts(surface))[0] ?? null;
            return {
                surface,
                ...(accountId ? { accountId } : account?.accountId ? { accountId: account.accountId } : {}),
                action,
                ok: account !== null,
                ...(account ? { state: account.state, authState: account.authState } : {}),
                account,
                message: account ? 'Account posture inspected.' : 'No matching channel account was found.',
                metadata: {},
            };
        }
        return null;
    }
    async authorizeActorAction(surface, request) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return null;
        if (plugin.authorizeActorAction)
            return plugin.authorizeActorAction(request);
        if (plugin.getActionAvailabilityState)
            return plugin.getActionAvailabilityState(request);
        const account = request.accountId ? await this.getAccount(surface, request.accountId) : (await this.listAccounts(surface))[0] ?? null;
        const allowed = Boolean(account?.enabled && account.configured);
        return {
            allowed,
            reason: allowed ? 'Account is configured and enabled.' : 'No configured enabled account is available for this surface.',
            account,
            actionAvailable: allowed,
            metadata: { fallback: true },
        };
    }
    async listCapabilities(surface) {
        const plugins = surface ? [this.getBySurface(surface)].filter((value) => value !== null) : this.list();
        const capabilities = [];
        for (const plugin of plugins) {
            if (!plugin.listCapabilities)
                continue;
            capabilities.push(...await plugin.listCapabilities());
        }
        return capabilities.sort((a, b) => a.surface.localeCompare(b.surface) || a.scope.localeCompare(b.scope) || a.label.localeCompare(b.label));
    }
    async listTools(surface) {
        const plugins = surface ? [this.getBySurface(surface)].filter((value) => value !== null) : this.list();
        const tools = [];
        for (const plugin of plugins) {
            if (!plugin.listTools)
                continue;
            tools.push(...await plugin.listTools());
        }
        return tools.sort((a, b) => a.surface.localeCompare(b.surface) || a.name.localeCompare(b.name));
    }
    async runTool(surface, toolId, input) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.runTool)
            return null;
        return plugin.runTool(toolId, input);
    }
    async listOperatorActions(surface) {
        const plugins = surface ? [this.getBySurface(surface)].filter((value) => value !== null) : this.list();
        const actions = [];
        for (const plugin of plugins) {
            if (!plugin.listOperatorActions)
                continue;
            actions.push(...await plugin.listOperatorActions());
        }
        return actions.sort((a, b) => a.surface.localeCompare(b.surface) || a.label.localeCompare(b.label));
    }
    async runOperatorAction(surface, actionId, input) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.runOperatorAction)
            return null;
        return plugin.runOperatorAction(actionId, input);
    }
    listAgentTools(surface) {
        const plugins = surface ? [this.getBySurface(surface)].filter((value) => value !== null) : this.list();
        const tools = [];
        const seen = new Set();
        for (const plugin of plugins) {
            if (!plugin.listAgentTools)
                continue;
            for (const tool of plugin.listAgentTools()) {
                if (seen.has(tool.definition.name))
                    continue;
                seen.add(tool.definition.name);
                tools.push(tool);
            }
        }
        return tools.sort((a, b) => a.definition.name.localeCompare(b.definition.name));
    }
    async resolveTarget(surface, options) {
        const plugin = this.getBySurface(surface);
        const input = options.input.trim();
        if (!plugin || input.length === 0)
            return null;
        if (plugin.resolveTarget) {
            const resolved = await plugin.resolveTarget(options);
            if (resolved)
                return resolved;
        }
        const explicit = plugin.parseExplicitTarget ? await plugin.parseExplicitTarget(input, options) : null;
        if (explicit)
            return explicit;
        const directory = await this.queryDirectory(surface, {
            query: input,
            limit: 1,
            ...(options.preferredKind === 'direct' ? { scope: 'users' } : {}),
            ...(options.preferredKind === 'group' || options.preferredKind === 'channel' ? { scope: 'groups' } : {}),
            ...(options.threadId ? { scope: 'threads' } : {}),
        });
        const entry = directory[0];
        if (entry) {
            const kind = entry.kind === 'user' || entry.kind === 'self' || entry.kind === 'member'
                ? 'direct'
                : entry.kind === 'thread'
                    ? 'thread'
                    : entry.kind === 'channel'
                        ? 'channel'
                        : entry.kind === 'group'
                            ? 'group'
                            : 'service';
            const target = {
                surface,
                input: options.input,
                normalized: input.toLowerCase(),
                kind,
                to: entry.handle ?? entry.id,
                display: entry.label,
                accountId: entry.accountId ?? options.accountId,
                workspaceId: entry.workspaceId,
                channelId: entry.kind === 'channel' || entry.kind === 'group' ? entry.groupId ?? entry.id : undefined,
                groupId: entry.groupId,
                threadId: options.threadId ?? entry.threadId,
                parentId: entry.parentId,
                directoryEntryId: entry.id,
                source: 'directory',
                sessionTarget: `channel:${surface}:${entry.id}`,
                metadata: { directoryEntry: entry },
            };
            const sessionTarget = plugin.resolveSessionTarget ? await plugin.resolveSessionTarget(target, options) : null;
            return sessionTarget ? { ...target, sessionTarget } : target;
        }
        const inferredKind = plugin.inferTargetConversationKind
            ? await plugin.inferTargetConversationKind(input, options)
            : options.preferredKind ?? 'service';
        if (!options.createIfMissing) {
            return {
                surface,
                input: options.input,
                normalized: input.toLowerCase(),
                kind: inferredKind ?? options.preferredKind ?? 'service',
                to: input,
                accountId: options.accountId,
                threadId: options.threadId,
                sessionTarget: `channel:${surface}:${input.toLowerCase()}`,
                source: 'miss',
                metadata: { fallback: true },
            };
        }
        return {
            surface,
            input: options.input,
            normalized: input.toLowerCase(),
            kind: inferredKind ?? options.preferredKind ?? 'service',
            to: input,
            accountId: options.accountId,
            threadId: options.threadId,
            sessionTarget: `channel:${surface}:${input.toLowerCase()}`,
            source: 'synthetic',
            metadata: { fallback: true },
        };
    }
    async resolveSessionTarget(surface, target, options) {
        const plugin = this.getBySurface(surface);
        if (!plugin?.resolveSessionTarget)
            return target.sessionTarget ?? null;
        return plugin.resolveSessionTarget(target, options);
    }
    async resolveParentConversationCandidates(surface, options) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return [];
        if (plugin.resolveParentConversationCandidates)
            return plugin.resolveParentConversationCandidates(options);
        const resolved = await this.resolveTarget(surface, options);
        return resolved ? [resolved] : [];
    }
    async lookupDirectory(surface, query, options) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return [];
        if (plugin.lookupDirectory)
            return plugin.lookupDirectory(query, options);
        if (plugin.queryDirectory)
            return plugin.queryDirectory({ ...options, query });
        return [];
    }
    async queryDirectory(surface, options) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return [];
        if (options.scope === 'members' && options.groupId && plugin.listGroupMembers) {
            return plugin.listGroupMembers(options.groupId, options);
        }
        if (plugin.queryDirectory)
            return plugin.queryDirectory(options);
        if (plugin.lookupDirectory)
            return plugin.lookupDirectory(options.query ?? '', options);
        return [];
    }
    async listGroupMembers(surface, groupId, options = {}) {
        const plugin = this.getBySurface(surface);
        if (!plugin)
            return [];
        if (plugin.listGroupMembers) {
            return plugin.listGroupMembers(groupId, options);
        }
        if (plugin.queryDirectory) {
            return plugin.queryDirectory({
                ...options,
                scope: 'members',
                groupId,
            });
        }
        return [];
    }
}
