/**
 * NeuroLink Unified Logger Utility
 *
 * Centralized logging for the entire NeuroLink ecosystem.
 * Provides structured logging with different severity levels and consistent formatting.
 * Supports both CLI --debug flag and NEUROLINK_DEBUG environment variable.
 * Maintains compatibility with MCP logging while providing enhanced features.
 *
 * Features:
 * - Multiple log levels (debug, info, warn, error)
 * - Log history retention with configurable limits
 * - Conditional logging based on environment settings
 * - Structured data support for complex objects
 * - Tabular data display
 */
// OTel trace context for log correlation (optional — gracefully no-ops if OTel not initialized)
let traceApi = null;
let traceApiPromise = null;
async function getTraceApi() {
    if (!traceApiPromise) {
        traceApiPromise = import("@opentelemetry/api")
            .then((mod) => {
            traceApi = mod;
            return mod;
        })
            .catch(() => null);
    }
    return traceApiPromise;
}
// Eagerly kick off the import so the cached value is available for synchronous callers
void getTraceApi();
// Pre-computed uppercase log levels for performance optimization
const UPPERCASE_LOG_LEVELS = {
    debug: "DEBUG",
    info: "INFO",
    warn: "WARN",
    error: "ERROR",
};
class NeuroLinkLogger {
    logLevel = "info";
    logs = [];
    maxLogs = 1000;
    isDebugMode;
    eventEmitter;
    constructor() {
        // Cache debug mode check to avoid repeated array searches
        this.isDebugMode =
            process.argv.includes("--debug") ||
                process.env.NEUROLINK_DEBUG === "true";
        // Check NEUROLINK_LOG_LEVEL for consistency with the unified NeuroLink logger
        const envLevel = process.env.NEUROLINK_LOG_LEVEL?.toLowerCase();
        if (envLevel && ["debug", "info", "warn", "error"].includes(envLevel)) {
            this.logLevel = envLevel;
        }
    }
    /**
     * Sets the event emitter that will receive log events.
     * When set, all log operations will emit a "log-event" event.
     *
     * @param emitter - The event emitter instance
     */
    setEventEmitter(emitter) {
        this.eventEmitter = emitter;
    }
    /**
     * Clears the event emitter reference.
     * Should be called when a NeuroLink instance is disposed to prevent memory leaks.
     */
    clearEventEmitter() {
        this.eventEmitter = undefined;
    }
    /**
     * Sets the minimum log level that will be processed and output.
     * Log messages with a level lower than this will be ignored.
     *
     * @param level - The minimum log level to process ("debug", "info", "warn", or "error")
     */
    setLogLevel(level) {
        this.logLevel = level;
    }
    /**
     * Determines whether a message with the given log level should be processed.
     * This method considers both the configured log level and the current debug mode.
     *
     * Logic:
     * 1. If not in debug mode, only error messages are allowed
     * 2. If in debug mode, messages at or above the configured log level are allowed
     *
     * @param level - The log level to check
     * @returns True if a message with this level should be logged, false otherwise
     */
    shouldLog(level) {
        // Dynamic debug mode check to handle CLI middleware timing
        const currentDebugMode = process.argv.includes("--debug") ||
            process.env.NEUROLINK_DEBUG === "true";
        // Hide all logs except errors unless debugging
        if (!currentDebugMode && level !== "error") {
            return false;
        }
        const levels = ["debug", "info", "warn", "error"];
        return levels.indexOf(level) >= levels.indexOf(this.logLevel);
    }
    /**
     * Generates a standardized prefix for log messages.
     * The prefix includes a timestamp and the log level in a consistent format.
     *
     * @param timestamp - ISO string representation of the log timestamp
     * @param level - The log level for this message
     * @returns Formatted prefix string like "[2025-08-18T13:45:30.123Z] [NEUROLINK:ERROR]"
     */
    getLogPrefix(timestamp, level) {
        return `[${timestamp}] [NEUROLINK:${UPPERCASE_LOG_LEVELS[level]}]`;
    }
    /**
     * Extracts current OTel trace context (trace_id, span_id) if available.
     * Returns empty object if OTel is not initialized or no active span exists.
     */
    getTraceContext() {
        if (!traceApi) {
            return {};
        }
        try {
            const span = traceApi.trace.getSpan(traceApi.context.active());
            if (!span) {
                return {};
            }
            const spanContext = span.spanContext();
            if (!spanContext ||
                spanContext.traceId === "00000000000000000000000000000000") {
                return {};
            }
            return {
                trace_id: spanContext.traceId,
                span_id: spanContext.spanId,
                trace_flags: String(spanContext.traceFlags),
            };
        }
        catch {
            return {};
        }
    }
    /**
     * Safely serialize data to fully expanded JSON string.
     * Handles circular references and non-serializable values.
     * Zero truncation — all nested objects and arrays are fully expanded.
     */
    serializeData(data) {
        if (data === undefined || data === null) {
            return String(data);
        }
        if (typeof data !== "object") {
            return String(data);
        }
        try {
            return JSON.stringify(data, (_key, value) => {
                if (value instanceof Error) {
                    return {
                        name: value.name,
                        message: value.message,
                        stack: value.stack,
                    };
                }
                return value;
            });
        }
        catch {
            // Handle circular references using a stack-based approach
            // to avoid false "[Circular]" on diamond (shared) references
            const ancestors = [];
            try {
                return JSON.stringify(data, function (_key, value) {
                    if (value instanceof Error) {
                        return {
                            name: value.name,
                            message: value.message,
                            stack: value.stack,
                        };
                    }
                    if (typeof value === "object" && value !== null) {
                        // Only flag actual circular (ancestor) references
                        if (ancestors.includes(value)) {
                            return "[Circular]";
                        }
                        ancestors.push(value);
                    }
                    return value;
                });
            }
            catch {
                return "[Unserializable Object]";
            }
        }
    }
    /**
     * Outputs a log entry to the console based on the log level.
     * Data is fully serialized to JSON — no [Object] or [Array] truncation.
     *
     * @param level - The log level (debug, info, warn, error).
     * @param prefix - The formatted log prefix.
     * @param message - The log message.
     * @param data - Optional additional data to log.
     */
    outputToConsole(level, prefix, message, data) {
        const logMethod = {
            debug: console.debug,
            info: console.info,
            warn: console.warn,
            error: console.error,
        }[level];
        const traceCtx = this.getTraceContext();
        const tracePrefix = traceCtx.trace_id
            ? ` [trace_id=${traceCtx.trace_id} span_id=${traceCtx.span_id}]`
            : "";
        if (data !== undefined && data !== null) {
            logMethod(prefix + tracePrefix, message, this.serializeData(data));
        }
        else {
            logMethod(prefix + tracePrefix, message);
        }
    }
    /**
     * Core internal logging method that handles:
     * 1. Creating log entries with consistent format
     * 2. Storing entries in the log history
     * 3. Managing log rotation to prevent memory issues
     * 4. Outputting formatted logs to the console
     * 5. Emitting log events if an event emitter is configured
     *
     * This is the central method called by all specific logging methods (debug, info, etc.)
     *
     * @param level - The severity level for this log entry
     * @param message - The message text to log
     * @param data - Optional additional context data to include
     */
    log(level, message, data) {
        if (!this.shouldLog(level)) {
            return;
        }
        const entry = {
            level,
            message,
            timestamp: new Date(),
            data,
        };
        // Emit log event if emitter is configured
        if (this.eventEmitter) {
            try {
                this.eventEmitter.emit("log-event", {
                    level,
                    message,
                    timestamp: new Date().getTime(),
                    data,
                });
            }
            catch {
                // Silently ignore emitter errors to avoid disrupting logging
            }
        }
        // Store log entry
        this.logs.push(entry);
        // Trim old logs
        if (this.logs.length > this.maxLogs) {
            this.logs = this.logs.slice(-this.maxLogs);
        }
        // Console output
        const timestamp = entry.timestamp.toISOString();
        const prefix = this.getLogPrefix(timestamp, level);
        this.outputToConsole(level, prefix, message, data);
    }
    /**
     * Logs a message at the debug level.
     * Used for detailed troubleshooting information.
     *
     * @param message - The message to log
     * @param data - Optional additional context data
     */
    debug(message, data) {
        this.log("debug", message, data);
    }
    /**
     * Logs a message at the info level.
     * Used for general information about system operation.
     *
     * @param message - The message to log
     * @param data - Optional additional context data
     */
    info(message, data) {
        this.log("info", message, data);
    }
    /**
     * Logs a message at the warn level.
     * Used for potentially problematic situations that don't prevent operation.
     *
     * @param message - The message to log
     * @param data - Optional additional context data
     */
    warn(message, data) {
        this.log("warn", message, data);
    }
    /**
     * Logs a message at the error level.
     * Used for critical issues that may cause failures.
     *
     * @param message - The message to log
     * @param data - Optional additional context data
     */
    error(message, data) {
        this.log("error", message, data);
    }
    /**
     * Retrieves stored log entries, optionally filtered by log level.
     * Returns a copy of the log entries to prevent external modification.
     *
     * @param level - Optional log level to filter by
     * @returns Array of log entries, either all or filtered by level
     */
    getLogs(level) {
        if (level) {
            return this.logs.filter((log) => log.level === level);
        }
        return [...this.logs];
    }
    /**
     * Removes all stored log entries.
     * Useful for testing or when log history is no longer needed.
     */
    clearLogs() {
        this.logs = [];
    }
    /**
     * Logs messages unconditionally using `console.log`.
     *
     * This method is part of a legacy simple logger interface for backward compatibility.
     * It bypasses the structured logging mechanism and should only be used when
     * unstructured, unconditional logging is required.
     *
     * Use with caution in production environments as it outputs to the console
     * regardless of the current log level or debug mode settings.
     *
     * Use cases:
     * - Critical system information that must always be visible
     * - Status messages during initialization before logging is fully configured
     * - Debugging in environments where normal logging might be suppressed
     *
     * @param args - The arguments to log. These are passed directly to `console.log`.
     */
    always(...args) {
        console.log(...args);
    }
    /**
     * Displays tabular data unconditionally using `console.table`.
     *
     * Similar to the `always` method, this bypasses log level checks and
     * will display data regardless of current logging settings.
     *
     * Important differences from other logging methods:
     * - Does NOT store entries in the log history
     * - Does NOT use the structured logging format with timestamps and prefixes
     * - Outputs directly to console without additional formatting
     *
     * Particularly useful for:
     * - Displaying structured data in a readable format during debugging
     * - Showing configuration options and their current values
     * - Presenting comparison data between different system states
     * - Performance metrics and timing data
     *
     * @param data - The data to display in table format. Can be an array of objects or an object with key-value pairs.
     */
    table(data) {
        console.table(data);
    }
}
// Export singleton instance to ensure consistent logging across the application
const neuroLinkLogger = new NeuroLinkLogger();
/**
 * Helper function to process logger arguments with minimal overhead.
 * Handles variable argument patterns and ensures safe serialization of objects.
 *
 * This function:
 * 1. Extracts the first argument as the message
 * 2. Handles serialization of non-string first arguments
 * 3. Collects remaining arguments as additional data
 * 4. Passes the processed arguments to the actual logging method
 *
 * @param args - Array of arguments passed to the logger
 * @param logMethod - Function that will perform the actual logging
 */
function processLoggerArgs(args, logMethod) {
    if (args.length === 0) {
        return;
    }
    // Serialize the first argument robustly to handle complex objects
    const message = (() => {
        try {
            return typeof args[0] === "string" ? args[0] : JSON.stringify(args[0]);
        }
        catch {
            return "[Unserializable Object]";
        }
    })();
    const data = args.length === 2 ? args[1] : args.length > 2 ? args.slice(1) : undefined;
    logMethod(message, data);
}
/**
 * Main unified logger export that provides a simplified API for logging.
 * This is the primary interface that should be used by application code.
 *
 * Features:
 * - Convenient logging methods (debug, info, warn, error)
 * - Unconditional logging (always, table)
 * - Log level control and configuration
 * - Log history management
 * - Event emission for all log operations (when emitter is configured)
 */
export const logger = {
    debug: (...args) => {
        if (neuroLinkLogger.shouldLog("debug")) {
            processLoggerArgs(args, (message, data) => neuroLinkLogger.debug(message, data));
        }
    },
    info: (...args) => {
        if (neuroLinkLogger.shouldLog("info")) {
            processLoggerArgs(args, (message, data) => neuroLinkLogger.info(message, data));
        }
    },
    warn: (...args) => {
        if (neuroLinkLogger.shouldLog("warn")) {
            processLoggerArgs(args, (message, data) => neuroLinkLogger.warn(message, data));
        }
    },
    error: (...args) => {
        if (neuroLinkLogger.shouldLog("error")) {
            processLoggerArgs(args, (message, data) => neuroLinkLogger.error(message, data));
        }
    },
    always: (...args) => {
        neuroLinkLogger.always(...args);
    },
    table: (data) => {
        neuroLinkLogger.table(data);
    },
    // Expose log-level check for gating expensive operations
    shouldLog: (level) => neuroLinkLogger.shouldLog(level),
    // Expose structured logging methods
    setLogLevel: (level) => neuroLinkLogger.setLogLevel(level),
    getLogs: (level) => neuroLinkLogger.getLogs(level),
    clearLogs: () => neuroLinkLogger.clearLogs(),
    setEventEmitter: (emitter) => neuroLinkLogger.setEventEmitter(emitter),
    clearEventEmitter: () => neuroLinkLogger.clearEventEmitter(),
};
/**
 * MCP compatibility exports - all use the same unified logger instance.
 * These exports maintain backward compatibility with code that expects
 * separate loggers for different MCP components, while actually using
 * the same underlying logger instance.
 */
export const mcpLogger = neuroLinkLogger;
export const autoDiscoveryLogger = neuroLinkLogger;
export const registryLogger = neuroLinkLogger;
export const unifiedRegistryLogger = neuroLinkLogger;
/**
 * Sets the global log level for all MCP-related logging.
 * This function provides a convenient way to adjust logging verbosity
 * for all MCP components at once.
 *
 * @param level - The log level to set ("debug", "info", "warn", or "error")
 */
export function setGlobalMCPLogLevel(level) {
    neuroLinkLogger.setLogLevel(level);
}
/**
 * Export LogLevel enum for runtime use.
 * Provides type-safe log level constants for use in application code.
 *
 * Example usage:
 * ```
 * import { logger, LogLevels } from './logger';  // Import from your project's path
 *
 * // Using the LogLevels constants (recommended for type safety):
 * logger.setLogLevel(LogLevels.debug);
 *
 * // Or directly using string values:
 * logger.setLogLevel('debug');
 * ```
 */
export const LogLevels = {
    debug: "debug",
    info: "info",
    warn: "warn",
    error: "error",
};
