/**
 * @fileoverview Helpers for working with trace context across boundaries.
 * Provides utilities for W3C traceparent headers, distributed tracing,
 * custom span creation, and context propagation across async boundaries.
 * @module src/utils/telemetry/trace
 */
import { context as otContext, propagation, SpanStatusCode, trace, } from '@opentelemetry/api';
import { config } from '../../config/index.js';
import { requestContextService } from '../../utils/internal/requestContext.js';
/**
 * Builds a W3C `traceparent` header value from a `RequestContext` or the currently active span.
 * Uses `ctx.traceId`/`ctx.spanId` when provided; falls back to the active OTel span context.
 * The version field is always `00` and the flags byte is hardcoded to `01` (sampled).
 * Returns `undefined` when neither source yields a trace ID and span ID.
 *
 * @param ctx - Optional RequestContext containing `traceId` and `spanId`
 * @returns Formatted `traceparent` string (e.g., `00-<traceId>-<spanId>-01`), or `undefined`
 *
 * @example
 * ```typescript
 * const traceparent = buildTraceparent(requestContext);
 * if (traceparent) {
 *   fetch(url, { headers: { traceparent } });
 * }
 * ```
 */
export function buildTraceparent(ctx) {
    const traceId = ctx?.traceId ?? trace.getActiveSpan()?.spanContext().traceId;
    const spanId = ctx?.spanId ?? trace.getActiveSpan()?.spanContext().spanId;
    if (!traceId || !spanId)
        return;
    // We do not currently read flags reliably from context; assume sampled
    return `00-${traceId}-${spanId}-01`;
}
/**
 * Extracts W3C traceparent from headers and returns parsed trace/span IDs.
 * Returns undefined if header is missing or malformed.
 *
 * @param headers - Headers object (Web API Headers or plain object)
 * @returns Parsed traceparent info or undefined
 *
 * @example
 * ```typescript
 * // Extract trace context from incoming request
 * const trace = extractTraceparent(request.headers);
 * if (trace) {
 *   logger.info('Processing with parent trace', {
 *     traceId: trace.traceId,
 *     sampled: trace.sampled
 *   });
 * }
 * ```
 */
export function extractTraceparent(headers) {
    const headerValue = headers instanceof Headers ? headers.get('traceparent') : headers.traceparent;
    if (!headerValue)
        return;
    // W3C traceparent format: 00-{traceId}-{spanId}-{flags}
    const match = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/.exec(headerValue);
    if (!match?.[1] || !match[2] || !match[3])
        return;
    return {
        traceId: match[1],
        spanId: match[2],
        sampled: match[3] === '01',
    };
}
/**
 * Creates a child RequestContext with parent trace context extracted from headers.
 * Useful for propagating trace context from incoming HTTP requests.
 *
 * @param parentHeaders - Headers containing traceparent
 * @param operation - Operation name for the new context
 * @returns New RequestContext with inherited trace context
 *
 * @example
 * ```typescript
 * // In HTTP handler
 * const context = createContextWithParentTrace(
 *   request.headers,
 *   'handleApiRequest'
 * );
 * logger.info('Processing request', context);
 * ```
 */
export function createContextWithParentTrace(parentHeaders, operation) {
    const traceInfo = extractTraceparent(parentHeaders);
    return requestContextService.createRequestContext({
        operation,
        ...(traceInfo && {
            traceId: traceInfo.traceId,
            parentSpanId: traceInfo.spanId,
        }),
    });
}
/**
 * Injects the current active context into a carrier, returning it.
 * Useful for HTTP headers: pass an object and use resulting key/values.
 *
 * @param carrier - Object to inject context into (typically headers)
 * @returns Same carrier with injected trace context
 *
 * @example
 * ```typescript
 * const headers = injectCurrentContextInto({
 *   'Content-Type': 'application/json'
 * });
 * // headers now contains traceparent, tracestate, etc.
 * ```
 */
export function injectCurrentContextInto(carrier) {
    propagation.inject(otContext.active(), carrier);
    return carrier;
}
/**
 * Creates a new span for manual instrumentation with automatic error handling.
 * The span is automatically marked as OK on success or ERROR on exception.
 * Errors are recorded as exceptions and automatically propagated.
 *
 * @param operationName - Name of the span (e.g., 'database.query', 'external.api')
 * @param fn - Async function to execute within the span
 * @param attributes - Optional attributes to attach to the span
 * @returns Promise resolving to the function's return value
 *
 * @example
 * ```typescript
 * // Instrument a database query
 * const users = await withSpan(
 *   'database.query.users',
 *   async () => db.users.findMany(),
 *   { 'db.table': 'users', 'db.operation': 'select' }
 * );
 * ```
 */
export async function withSpan(operationName, fn, attributes) {
    const tracer = trace.getTracer(config.openTelemetry.serviceName, config.openTelemetry.serviceVersion);
    return await tracer.startActiveSpan(operationName, async (span) => {
        if (attributes) {
            span.setAttributes(attributes);
        }
        try {
            const result = await fn(span);
            span.setStatus({ code: SpanStatusCode.OK });
            return result;
        }
        catch (error) {
            span.recordException(error instanceof Error ? error : new Error(String(error)));
            span.setStatus({
                code: SpanStatusCode.ERROR,
                message: error instanceof Error ? error.message : String(error),
            });
            throw error;
        }
        finally {
            span.end();
        }
    });
}
/**
 * Runs a function within the currently active OpenTelemetry context.
 * Useful for carrying the active context across async boundaries (e.g., `setTimeout`, `queueMicrotask`).
 *
 * **Limitation:** When `ctx` contains trace IDs, this function executes within the
 * current active context rather than restoring `ctx`'s specific span context.
 * Full span restoration would require span recreation, which this utility intentionally avoids.
 * If `ctx` has no trace IDs, `fn` is called directly without any context wrapping.
 *
 * @param ctx - RequestContext containing trace IDs; if missing or lacking IDs, `fn` runs directly
 * @param fn - Function to execute within the active OTel context
 * @returns Result of `fn`
 *
 * @example
 * ```typescript
 * // Carry the active OTel context into a setTimeout callback
 * const context = requestContextService.createRequestContext({ operation: 'delayed' });
 * setTimeout(() => {
 *   runInContext(context, () => {
 *     logger.info('Still in trace context', context);
 *   });
 * }, 1000);
 * ```
 */
export function runInContext(ctx, fn) {
    // If no trace context, run directly
    if (!ctx?.traceId || !ctx?.spanId) {
        return fn();
    }
    // Execute within the active context
    // Note: Full context restoration would require span recreation
    // This simplified version maintains execution but doesn't create new spans
    return otContext.with(otContext.active(), fn);
}
//# sourceMappingURL=trace.js.map