/**
 * @fileoverview Utilities for creating and managing request contexts.
 *
 * A request context is an immutable-by-convention object that carries a unique
 * request ID, ISO 8601 timestamp, optional tenant ID, and optional auth data.
 * It is the primary carrier for log correlation, distributed tracing, and
 * multi-tenancy isolation throughout the server.
 *
 * Key behaviors:
 * - `requestId` is inherited from a parent context when provided, otherwise a
 *   new unique ID is generated via {@link generateRequestContextId}.
 * - `tenantId` is resolved with the following priority (highest to lowest):
 *   `additionalContext.tenantId` → spread rest params → `parentContext.tenantId`
 *   → AsyncLocalStorage auth store (`authContext`).
 * - OpenTelemetry `traceId` and `spanId` are automatically injected from the
 *   active span when one exists.
 * - Auth data is populated separately via {@link requestContextService.withAuthInfo}
 *   and is NOT stored in AsyncLocalStorage by this module — ALS propagation is
 *   the responsibility of auth middleware.
 *
 * @module src/utils/internal/requestContext
 */
import { trace } from '@opentelemetry/api';
import { authContext as alsAuthContext } from '../../mcp-server/transports/auth/lib/authContext.js';
import { generateRequestContextId } from '../../utils/security/idGenerator.js';
/** Maps validated token info to the context-facing auth shape. */
function toAuthContext(info) {
    return {
        sub: info.subject ?? info.clientId,
        scopes: info.scopes,
        clientId: info.clientId,
        ...(info.tenantId && { tenantId: info.tenantId }),
        ...(info.token && { token: info.token }),
    };
}
/**
 * Singleton-like service object for managing request context operations.
 * @private
 */
const requestContextServiceInstance = {
    /**
     * Creates a new {@link RequestContext} with a guaranteed unique `requestId`
     * and current ISO 8601 `timestamp`.
     *
     * Merge order (later entries win, except `requestId`/`timestamp` which are
     * always generated fresh or inherited):
     * 1. `parentContext` properties (base)
     * 2. Remaining spread params (everything in `params` except named fields)
     * 3. `additionalContext` properties (`requestId`/`timestamp` stripped)
     * 4. Resolved `requestId` and `timestamp` (canonical, never overwritten)
     * 5. Resolved `tenantId` (see priority order in {@link RequestContext.tenantId})
     * 6. `operation` (written as `context.operation` when provided)
     * 7. OTel `traceId`/`spanId` from the active span (when present)
     *
     * @param params - Context creation parameters. Defaults to `{}`.
     * @returns A fully constructed `RequestContext`.
     *
     * @example
     * ```typescript
     * // Minimal — generates a fresh requestId and timestamp
     * const ctx = requestContextService.createRequestContext();
     *
     * // Propagate an existing request ID across a call chain
     * const childCtx = requestContextService.createRequestContext({
     *   parentContext: incomingCtx,
     *   operation: 'processItem',
     *   additionalContext: { itemId: item.id },
     * });
     * ```
     */
    createRequestContext(params = {}) {
        const { parentContext, additionalContext, operation, ...rest } = params;
        const inheritedContext = parentContext && typeof parentContext === 'object' ? { ...parentContext } : {};
        const authStore = alsAuthContext.getStore();
        const tenantIdFromAuth = authStore?.authInfo?.tenantId;
        // Bridge auth info from ALS into the context so ctx.auth is populated
        // in tool/resource handlers without requiring a separate withAuthInfo() call.
        const authFromStore = authStore?.authInfo
            ? toAuthContext(authStore.authInfo)
            : undefined;
        const requestId = typeof inheritedContext.requestId === 'string' && inheritedContext.requestId
            ? inheritedContext.requestId
            : generateRequestContextId();
        const timestamp = new Date().toISOString();
        const extractTenantId = (obj) => obj != null && typeof obj.tenantId === 'string' ? obj.tenantId : undefined;
        const resolvedTenantId = extractTenantId(additionalContext) ??
            extractTenantId(rest) ??
            extractTenantId(inheritedContext) ??
            tenantIdFromAuth;
        // Strip system fields from additionalContext to prevent overwriting requestId/timestamp
        const { requestId: _r, timestamp: _t, ...safeAdditional } = additionalContext && typeof additionalContext === 'object' ? additionalContext : {};
        const context = {
            ...inheritedContext,
            ...rest, // Spread any other properties from the params object
            ...safeAdditional,
            requestId,
            timestamp,
            ...(resolvedTenantId ? { tenantId: resolvedTenantId } : {}),
            ...(operation && typeof operation === 'string' ? { operation } : {}),
            ...(authFromStore && { auth: authFromStore }),
        };
        // --- OpenTelemetry Integration ---
        const activeSpan = trace.getActiveSpan();
        if (activeSpan && typeof activeSpan.spanContext === 'function') {
            const spanContext = activeSpan.spanContext();
            if (spanContext) {
                context.traceId = spanContext.traceId;
                context.spanId = spanContext.spanId;
            }
        }
        // --- End OpenTelemetry Integration ---
        return context;
    },
    /**
     * Creates a new {@link RequestContext} enriched with authentication information.
     * This method populates the context with auth data from a validated token,
     * including tenant ID, client ID, scopes, and subject.
     *
     * **Note:** This method builds and returns a `RequestContext` object only.
     * It does NOT propagate auth into `AsyncLocalStorage`. ALS propagation is
     * handled by the auth middleware via `authContext.run()`. If you need
     * scope enforcement via `withRequiredScopes()`, ensure the call runs
     * inside the middleware's ALS continuation.
     *
     * @param authInfo - The validated authentication information from JWT/OAuth token.
     * @param parentContext - Optional parent context to inherit properties from.
     * @returns A new `RequestContext` object with auth information populated.
     *
     * @example
     * ```typescript
     * const authInfo = await jwtStrategy.verify(token);
     * const context = requestContextService.withAuthInfo(authInfo);
     * // context now includes: { requestId, timestamp, tenantId, auth: {...}, ... }
     * ```
     */
    withAuthInfo(authInfo, parentContext) {
        // Inherit the operation from parentContext when provided, so callers that
        // already established an operation name (e.g. 'AutoTaskHandler') keep it.
        const parentOp = parentContext && typeof parentContext.operation === 'string'
            ? parentContext.operation
            : undefined;
        const baseContext = this.createRequestContext({
            operation: parentOp ?? 'withAuthInfo',
            parentContext,
            additionalContext: {
                tenantId: authInfo.tenantId,
            },
        });
        return {
            ...baseContext,
            auth: toAuthContext(authInfo),
        };
    },
};
/**
 * Service for creating and enriching {@link RequestContext} instances.
 *
 * The two primary methods cover the full lifecycle:
 * - {@link requestContextService.createRequestContext} — general-purpose context
 *   creation with parent propagation and OTel integration.
 * - {@link requestContextService.withAuthInfo} — enriches a context with
 *   structured auth data from a validated token.
 *
 * @example
 * ```typescript
 * import { requestContextService } from '../../utils/internal/requestContext.js';
 *
 * // Create a root context for an incoming request
 * const ctx = requestContextService.createRequestContext({ operation: 'handleRequest' });
 *
 * // Enrich with auth after token verification
 * const authCtx = requestContextService.withAuthInfo(verifiedAuthInfo, ctx);
 * ```
 */
export const requestContextService = requestContextServiceInstance;
//# sourceMappingURL=requestContext.js.map