/**
 * Correlation ID management for tracking requests across components
 */
import { AsyncLocalStorage } from 'async_hooks';
import { randomBytes } from 'crypto';
/**
 * Async local storage for correlation context
 * Provides thread-safe context storage across async operations
 */
export const correlationStorage = new AsyncLocalStorage();
/**
 * Correlation Context Monitoring Metrics
 * Tracks usage, performance, and potential issues for production monitoring
 */
const correlationMonitoring = {
    contextsCreated: 0,
    activeContexts: 0,
    errors: 0,
    lastError: null,
    lastErrorTime: 0,
    startTime: Date.now(),
};
/**
 * Legacy context has been removed in favor of AsyncLocalStorage-only approach
 * This eliminates race conditions in concurrent operations
 * All code should now use withCorrelationContext() or withNewCorrelationContext()
 */
/**
 * Get correlation context monitoring metrics
 * Provides insights into context usage, performance, and health
 */
export function getCorrelationMonitoring() {
    return { ...correlationMonitoring };
}
/**
 * Log correlation context monitoring metrics
 * Useful for periodic logging of system health
 * @internal
 */
export function logCorrelationMonitoring(level = 'info') {
    const metrics = getCorrelationMonitoring();
    const uptime = Date.now() - metrics.startTime;
    const uptimeMinutes = Math.floor(uptime / (1000 * 60));
    const errorRate = metrics.contextsCreated > 0
        ? ((metrics.errors / metrics.contextsCreated) * 100).toFixed(2) + '%'
        : '0%';
    const message = `
[Correlation Monitoring] 
- Uptime: ${uptimeMinutes} minutes
- Contexts Created: ${metrics.contextsCreated}
- Active Contexts: ${metrics.activeContexts}
- Errors: ${metrics.errors}
- Error Rate: ${errorRate}
- Last Error: ${metrics.lastError || 'None'}`;
    switch (level) {
        case 'debug':
            console.debug(message);
            break;
        case 'info':
            console.info(message);
            break;
        case 'warn':
            console.warn(message);
            break;
        case 'error':
            console.error(message);
            break;
    }
}
/**
 * Reset correlation context monitoring metrics
 * Useful for testing or periodic reporting
 */
export function resetCorrelationMonitoring() {
    correlationMonitoring.contextsCreated = 0;
    correlationMonitoring.activeContexts = 0;
    correlationMonitoring.errors = 0;
    correlationMonitoring.lastError = null;
    correlationMonitoring.lastErrorTime = 0;
    correlationMonitoring.startTime = Date.now();
}
/**
 * Perform health check on correlation context system
 * Verifies that the AsyncLocalStorage context system is functioning properly
 */
export function checkCorrelationHealth() {
    try {
        // Test basic context creation and retrieval
        const testContext = {
            id: 'health-check-' + generateShortCorrelationId(),
            metadata: { healthCheck: true, timestamp: Date.now() },
        };
        let contextWorking = false;
        withCorrelationContext(testContext, () => {
            const current = getCurrentCorrelationContext();
            contextWorking = current?.id === testContext.id;
        });
        if (!contextWorking) {
            return {
                healthy: false,
                message: 'Correlation context system failed basic functionality test',
                metrics: getCorrelationMonitoring(),
            };
        }
        // Check for excessive errors
        const errorRate = correlationMonitoring.contextsCreated > 0
            ? correlationMonitoring.errors / correlationMonitoring.contextsCreated
            : 0;
        if (errorRate > 0.1) {
            // More than 10% error rate
            return {
                healthy: false,
                message: `High error rate detected: ${(errorRate * 100).toFixed(1)}%`,
                metrics: getCorrelationMonitoring(),
            };
        }
        return {
            healthy: true,
            message: 'Correlation context system is healthy',
            metrics: getCorrelationMonitoring(),
        };
    }
    catch (error) {
        return {
            healthy: false,
            message: `Health check failed: ${error instanceof Error ? error.message : String(error)}`,
            metrics: getCorrelationMonitoring(),
        };
    }
}
/**
 * Generate a new correlation ID
 */
export function generateCorrelationId() {
    // Generate 16-byte random hex string (32 characters)
    return randomBytes(16).toString('hex');
}
/**
 * Generate a short correlation ID (8 characters) for display
 */
export function generateShortCorrelationId() {
    // Generate 4-byte random hex string (8 characters)
    return randomBytes(4).toString('hex');
}
/**
 * Create a new correlation context with specific ID
 */
export function createCorrelationContextWithId(id, metadata) {
    return {
        id,
        metadata,
    };
}
/**
 * Create a new correlation context with optional parent ID
 */
export function createCorrelationContext(parentId, metadata) {
    return {
        id: generateCorrelationId(),
        parentId,
        metadata,
    };
}
/**
 * Get the current correlation context
 * Now uses AsyncLocalStorage exclusively - no legacy fallback
 */
export function getCurrentCorrelationContext() {
    const asyncContext = correlationStorage.getStore();
    return asyncContext || null;
}
/**
 * Run a function with a specific correlation context
 */
export function withCorrelationContext(context, fn) {
    try {
        if (process.env.NANOCODER_CORRELATION_DEBUG === 'true') {
            console.debug(`[Correlation] Context started: ${context.id}`);
        }
        correlationMonitoring.activeContexts++;
        const result = correlationStorage.run(context, fn);
        correlationMonitoring.activeContexts--;
        if (process.env.NANOCODER_CORRELATION_DEBUG === 'true') {
            console.debug(`[Correlation] Context completed: ${context.id}`);
        }
        return result;
    }
    catch (error) {
        correlationMonitoring.errors++;
        correlationMonitoring.lastError =
            error instanceof Error ? error.message : String(error);
        correlationMonitoring.lastErrorTime = Date.now();
        correlationMonitoring.activeContexts--;
        if (process.env.NANOCODER_CORRELATION_DEBUG === 'true') {
            console.error(`[Correlation] Context error: ${context.id}`, error); // nosemgrep
        }
        throw error;
    }
}
/**
 * Run a function with a new correlation context
 */
export function withNewCorrelationContext(fn, correlationId, metadata) {
    const context = correlationId
        ? createCorrelationContextWithId(correlationId, metadata)
        : createCorrelationContext(undefined, metadata);
    correlationMonitoring.contextsCreated++;
    if (process.env.NANOCODER_CORRELATION_DEBUG === 'true') {
        console.debug(`[Correlation] New context created: ${context.id}`);
    }
    try {
        correlationMonitoring.activeContexts++;
        const result = correlationStorage.run(context, () => fn(context));
        correlationMonitoring.activeContexts--;
        if (process.env.NANOCODER_CORRELATION_DEBUG === 'true') {
            console.debug(`[Correlation] New context completed: ${context.id}`);
        }
        return result;
    }
    catch (error) {
        correlationMonitoring.errors++;
        correlationMonitoring.lastError =
            error instanceof Error ? error.message : String(error);
        correlationMonitoring.lastErrorTime = Date.now();
        correlationMonitoring.activeContexts--;
        if (process.env.NANOCODER_CORRELATION_DEBUG === 'true') {
            console.error(`[Correlation] New context error: ${context.id}`, error); // nosemgrep
        }
        throw error;
    }
}
/**
 * Get the correlation ID for the current context
 */
export function getCorrelationId() {
    const asyncContext = correlationStorage.getStore();
    return asyncContext?.id || null;
}
/**
 * Check if correlation is enabled
 */
export function isCorrelationEnabled() {
    return process.env.NANOCODER_CORRELATION_ENABLED !== 'false';
}
/**
 * Get correlation header for HTTP requests
 */
export function getCorrelationHeader() {
    const correlationId = getCorrelationId();
    if (!correlationId || !isCorrelationEnabled()) {
        return {};
    }
    return {
        'X-Correlation-ID': correlationId,
    };
}
/**
 * Extract correlation ID from HTTP headers
 */
export function extractCorrelationId(headers) {
    // Try various header names
    const possibleHeaders = [
        'x-correlation-id',
        'x-request-id',
        'x-trace-id',
        'x-span-id',
        'correlation-id',
        'request-id',
        'trace-id',
        'span-id',
    ];
    for (const header of possibleHeaders) {
        const value = headers[header] || headers[header.toUpperCase()];
        if (value && typeof value === 'string') {
            return value;
        }
    }
    return null;
}
/**
 * Create a correlation context from HTTP headers
 */
export function createCorrelationFromHeaders(headers, metadata) {
    if (!isCorrelationEnabled()) {
        return null;
    }
    const correlationId = extractCorrelationId(headers);
    if (correlationId) {
        return createCorrelationContextWithId(correlationId, metadata);
    }
    return createCorrelationContext(undefined, metadata);
}
/**
 * Get correlation metadata
 */
export function getCorrelationMetadata(key) {
    const asyncContext = correlationStorage.getStore();
    if (asyncContext?.metadata) {
        return key ? asyncContext.metadata[key] : asyncContext.metadata;
    }
    return key ? undefined : {};
}
/**
 * Format correlation context for logging
 */
export function formatCorrelationForLog() {
    const asyncContext = correlationStorage.getStore();
    if (asyncContext && isCorrelationEnabled()) {
        const result = {
            correlationId: asyncContext.id,
        };
        if (asyncContext.parentId) {
            result.parentCorrelationId = asyncContext.parentId;
        }
        return result;
    }
    return {};
}
/**
 * Correlation middleware for Express-like frameworks
 */
export function correlationMiddleware() {
    return (req, res, next) => {
        // Extract or create correlation ID
        let correlationId = extractCorrelationId(req.headers || {});
        if (!correlationId) {
            correlationId = generateCorrelationId();
        }
        // Create correlation context with the extracted or generated ID
        const context = createCorrelationContextWithId(correlationId, {
            method: req.method,
            url: req.url,
            userAgent: req.headers?.['user-agent'],
        });
        // Run the request handler within the correlation context
        return correlationStorage.run(context, () => {
            // Add correlation ID to response headers
            if (res.setHeader) {
                res.setHeader('X-Correlation-ID', correlationId);
            }
            next();
        });
    };
}
/**
 * Wrap an async function with correlation tracking
 */
export function withCorrelation(fn, getCorrelationIdFromArgs) {
    return (async (...args) => {
        let context = null;
        // Try to get correlation ID from arguments if provided
        if (getCorrelationIdFromArgs) {
            const correlationId = getCorrelationIdFromArgs(...args);
            if (correlationId) {
                context = createCorrelationContextWithId(correlationId);
            }
        }
        // If no context from args, try current context or create new
        if (!context) {
            const current = getCurrentCorrelationContext();
            if (current) {
                context = createCorrelationContext(current.id);
            }
            else if (isCorrelationEnabled()) {
                context = createCorrelationContext();
            }
        }
        // Run function within correlation context
        if (context) {
            return correlationStorage.run(context, async () => {
                return await fn(...args);
            });
        }
        else {
            return await fn(...args);
        }
    });
}
//# sourceMappingURL=correlation.js.map