/**
 * @fileoverview Pure helpers for the MCP dashboard's AI-agent integration.
 *
 * These functions are intentionally free of Angular / component dependencies so
 * they can be unit-tested in isolation. The component
 * ({@link MCPDashboardComponent}) supplies a plain snapshot of its current state
 * and these helpers shape it into the flat key-value `AgentContext` object that
 * flows to the async chat agent and the realtime co-agent via
 * `NavigationService.SetAgentContext`.
 *
 * The shape is now DEEP (Data-Explorer depth): per active tab the helper publishes
 * the tab's counts, selected-item id+name, bounded visible NAMES (cap 25 with a
 * companion total-count when truncated), search/filter state, and the tools view
 * mode — so the agent can SEE the server / connection / tool / log names and the
 * one that's selected, then act via the mode-scoped client tools.
 *
 * 🚨 SAFETY BOUNDARY — NO SECRETS, EVER 🚨
 * MCP is a credential-bearing surface: servers carry OAuth client secrets,
 * connections carry bearer tokens, and there are dedicated OAuth token / client
 * registration / credential entities in scope. The context built here exposes
 * ONLY aggregate counts, navigation state, the user's own search/filter terms,
 * and user-facing DISPLAY names — never a server URL, command, bearer token,
 * OAuth client secret, hashed/encrypted credential, or any tool input/output
 * payload. If you add a field to one of the *ContextInput interfaces, verify it
 * carries no secret/credential value before doing so.
 */
/** The four MCP dashboard tabs. */
export const MCP_TABS = ['servers', 'connections', 'tools', 'logs'];
/** The two tools-view modes. */
export const MCP_TOOLS_VIEW_MODES = ['card', 'list'];
/**
 * The sortable columns of the execution-logs table, mirrored here so the
 * `SortLogs` client tool has a single Angular-free source of truth for its
 * tolerant {@link validateEnumParam} guard. Must stay in sync with the
 * component's `LogsSortColumn` union.
 */
export const MCP_LOG_SORT_COLUMNS = ['status', 'server', 'tool', 'connection', 'started', 'duration', 'error'];
/**
 * Upper bound on how many display names we publish in a name-list context field
 * (VisibleServerNames, VisibleToolNames, …). Keeping the streamed note bounded
 * avoids flooding the co-agent with hundreds of names; when the underlying list
 * is larger we surface a companion total-count field so the agent still knows
 * the true size.
 */
export const MCP_AGENT_CONTEXT_NAME_LIST_CAP = 25;
/**
 * Type-guard / validator for an MCP tab string. Keeps the `SwitchMCPTab`
 * client tool tolerant of arbitrary agent input — only the four known tabs are
 * accepted.
 */
export function isValidMCPTab(tab) {
    return typeof tab === 'string' && MCP_TABS.includes(tab);
}
/**
 * Type-guard / validator for a tools-view-mode string. Keeps the
 * `SetToolsViewMode` client tool tolerant of arbitrary agent input.
 */
export function isValidToolsViewMode(mode) {
    return typeof mode === 'string' && MCP_TOOLS_VIEW_MODES.includes(mode);
}
/**
 * Cap an array of names to {@link MCP_AGENT_CONTEXT_NAME_LIST_CAP} entries.
 * Pure + deterministic so the context shape stays unit-testable. Never throws.
 *
 * @param names - the full list of names (the caller owns de-duplication / ordering)
 * @returns the first N names; a new array (never mutates the input)
 */
export function capMCPNames(names) {
    return names.slice(0, MCP_AGENT_CONTEXT_NAME_LIST_CAP);
}
/**
 * Resolve an agent-supplied reference to one of the available items, the way a
 * user names things. The agent may pass an exact ID, an exact name, or a partial
 * name — so we try, in order:
 *   1. exact ID match (case-insensitive, to tolerate SQL-Server-upper vs PG-lower UUID casing)
 *   2. exact name match (case-insensitive, whitespace-trimmed)
 *   3. first case-insensitive *contains* match on the name
 *
 * Pure + deterministic over the supplied candidate list, so it's unit-testable
 * in isolation. Returns the matched item, or null on a miss.
 *
 * @param input - whatever the agent passed (an ID or a display name)
 * @param candidates - the items available on this surface
 */
export function resolveMCPItem(input, candidates) {
    const needle = (input ?? '').trim().toLowerCase();
    if (!needle) {
        return null;
    }
    const byId = candidates.find(c => c.ID.toLowerCase() === needle);
    if (byId) {
        return byId;
    }
    const byName = candidates.find(c => c.Name.trim().toLowerCase() === needle);
    if (byName) {
        return byName;
    }
    return candidates.find(c => c.Name.toLowerCase().includes(needle)) ?? null;
}
/**
 * Build a tolerant "not found" error message that samples a few of the available
 * names, so the agent can correct itself. Pure + deterministic.
 *
 * @param input - the agent-supplied reference that didn't resolve
 * @param candidates - the available items (their names are sampled)
 * @param noun - the kind of thing (e.g. "server", "tool") for the message
 */
export function buildMCPNotFoundError(input, candidates, noun) {
    const sample = candidates.slice(0, 6).map(c => c.Name).join(', ');
    return `No ${noun} matching "${input}" is available. Available ${noun}s include: ${sample || '(none)'}.`;
}
/**
 * Build the always-on (top-level) slice of the MCP agent context.
 *
 * Kept a pure function (no `this`) so the context shape is unit-testable and
 * decoupled from change-detection timing. Returns a flat key-value object.
 *
 * @param input - the component's current top-level state snapshot (counts + nav state only)
 * @returns a flat, secret-free key-value object
 */
export function buildMCPAgentContext(input) {
    return {
        ActiveTab: input.ActiveTab,
        ServerCount: input.ServerCount,
        ActiveServerCount: input.ActiveServerCount,
        ConnectionCount: input.ConnectionCount,
        ToolCount: input.ToolCount,
        RecentExecutionCount: input.RecentExecutionCount,
        FailedExecutionCount: input.FailedExecutionCount,
        CurrentSearchTerm: input.CurrentSearchTerm,
        ToolsViewMode: input.ToolsViewMode,
    };
}
// ============================================================================
// Per-tab DEEP context slices
// ============================================================================
/**
 * Append a bounded name list under `key` plus, when the list was truncated, a
 * companion `<key>Count` reporting the true total. Mutates `target` in place and
 * returns it for chaining. Pure aside from the in-place write the caller asked for.
 */
function withBoundedNames(target, key, names) {
    if (names.length === 0) {
        return target;
    }
    target[key] = capMCPNames(names);
    if (names.length > MCP_AGENT_CONTEXT_NAME_LIST_CAP) {
        target[`${key}Count`] = names.length;
    }
    return target;
}
/** Build the Servers-tab deep context slice. */
export function buildMCPServersContext(input) {
    const ctx = {
        Surface: 'servers',
        ServerCount: input.ServerCount,
        FilteredServerCount: input.FilteredServerCount,
        ActiveServerCount: input.ActiveServerCount,
        ServerStatusFilter: input.ServerStatusFilter,
        SelectedServerId: input.SelectedServerId,
        SelectedServerName: input.SelectedServerName,
    };
    return withBoundedNames(ctx, 'VisibleServerNames', input.VisibleServerNames);
}
/** Build the Connections-tab deep context slice. */
export function buildMCPConnectionsContext(input) {
    const ctx = {
        Surface: 'connections',
        ConnectionCount: input.ConnectionCount,
        FilteredConnectionCount: input.FilteredConnectionCount,
        ConnectionStatusFilter: input.ConnectionStatusFilter,
        SelectedConnectionId: input.SelectedConnectionId,
        SelectedConnectionName: input.SelectedConnectionName,
    };
    return withBoundedNames(ctx, 'VisibleConnectionNames', input.VisibleConnectionNames);
}
/** Build the Tools-tab deep context slice. */
export function buildMCPToolsContext(input) {
    const ctx = {
        Surface: 'tools',
        ToolCount: input.ToolCount,
        FilteredToolCount: input.FilteredToolCount,
        ToolsViewMode: input.ToolsViewMode,
        ToolStatusFilter: input.ToolStatusFilter,
        ServerFilter: input.ServerFilter,
        ServerFilterName: input.ServerFilterName,
        CategoryFilter: input.CategoryFilter,
        FavoritesOnly: input.FavoritesOnly,
        RecentOnly: input.RecentOnly,
        FavoriteToolCount: input.FavoriteToolCount,
        SelectedToolId: input.SelectedToolId,
        SelectedToolName: input.SelectedToolName,
    };
    withBoundedNames(ctx, 'VisibleToolNames', input.VisibleToolNames);
    withBoundedNames(ctx, 'AvailableServerNames', input.AvailableServerNames);
    withBoundedNames(ctx, 'AvailableCategoryNames', input.AvailableCategoryNames);
    return ctx;
}
/** Build the Logs-tab deep context slice. */
export function buildMCPLogsContext(input) {
    const ctx = {
        Surface: 'logs',
        ExecutionCount: input.ExecutionCount,
        FilteredExecutionCount: input.FilteredExecutionCount,
        FailedExecutionCount: input.FailedExecutionCount,
        LogStatusFilter: input.LogStatusFilter,
        SortColumn: input.SortColumn,
        SortDirection: input.SortDirection,
        SelectedLogId: input.SelectedLogId,
        SelectedLogName: input.SelectedLogName,
        DetailPanelOpen: input.DetailPanelOpen,
    };
    return withBoundedNames(ctx, 'VisibleLogLabels', input.VisibleLogLabels);
}
/**
 * Build the full MCP agent context: the always-on top-level slice merged with
 * the deep per-tab slice for the active tab. Pure + deterministic. The deep slice
 * keys take precedence on the (intentional) overlap of count fields, so the agent
 * sees the tab-scoped, filter-aware numbers.
 *
 * @param top - the always-on counts/tab/view-mode snapshot
 * @param tab - the active tab's deep snapshot (discriminated by `Tab`)
 * @returns a flat, secret-free key-value object suitable for `SetAgentContext`
 */
export function buildMCPAgentContextFull(top, tab) {
    const base = buildMCPAgentContext(top);
    const deep = buildMCPTabContext(tab);
    return { ...base, ...deep };
}
/** Build just the deep per-tab slice for the active tab. */
export function buildMCPTabContext(tab) {
    switch (tab.Tab) {
        case 'servers':
            return buildMCPServersContext(tab.Data);
        case 'connections':
            return buildMCPConnectionsContext(tab.Data);
        case 'tools':
            return buildMCPToolsContext(tab.Data);
        case 'logs':
            return buildMCPLogsContext(tab.Data);
    }
}
//# sourceMappingURL=mcp-agent-context.js.map