import { format } from 'node:util';
import { safeStringify } from '@agentuity/core';
import * as LogsAPI from '@opentelemetry/api-logs';
import ConsoleLogger from './logger/console';
/**
 * Reference to the original console object before patching.
 */
export const __originalConsole = Object.create(console);
export class OtelLogger {
    delegate;
    context;
    logger;
    logLevel;
    constructor(useConsole, delegate, context, logLevel) {
        this.delegate = delegate;
        this.context = context;
        this.logLevel = logLevel ?? 'info';
        this.logger = useConsole ? new ConsoleLogger(context, false, this.logLevel) : undefined;
    }
    formatMessage(message) {
        if (typeof message === 'string') {
            return message;
        }
        try {
            return safeStringify(message);
        }
        catch {
            // Handle circular references or other unknown stringification errors
            return String(message);
        }
    }
    getAttributes() {
        return this.context;
    }
    emit(severityNumber, severityText, body) {
        const attributes = this.getAttributes();
        try {
            this.delegate.emit({
                severityNumber,
                severityText,
                body,
                attributes: attributes,
            });
        }
        catch (error) {
            // Log error to console if available, but don't fail the entire operation
            this.logger?.error('Failed to emit log to OTLP instance:', error);
        }
    }
    shouldLog(level) {
        switch (this.logLevel) {
            case 'trace':
                return true;
            case 'debug':
                return (level === LogsAPI.SeverityNumber.DEBUG ||
                    level === LogsAPI.SeverityNumber.INFO ||
                    level === LogsAPI.SeverityNumber.WARN ||
                    level === LogsAPI.SeverityNumber.ERROR);
            case 'info':
                return (level === LogsAPI.SeverityNumber.INFO ||
                    level === LogsAPI.SeverityNumber.WARN ||
                    level === LogsAPI.SeverityNumber.ERROR);
            case 'warn':
                return level === LogsAPI.SeverityNumber.WARN || level === LogsAPI.SeverityNumber.ERROR;
            case 'error':
                return level === LogsAPI.SeverityNumber.ERROR;
        }
        return false;
    }
    trace(message, ...args) {
        if (!this.shouldLog(LogsAPI.SeverityNumber.TRACE)) {
            return;
        }
        this.logger?.trace(message, ...args);
        let body;
        try {
            body = format(this.formatMessage(message), ...args);
        }
        catch {
            // Fallback if format causes recursion
            body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
        }
        this.emit(LogsAPI.SeverityNumber.TRACE, 'TRACE', body);
    }
    debug(message, ...args) {
        if (!this.shouldLog(LogsAPI.SeverityNumber.DEBUG)) {
            return;
        }
        this.logger?.debug(message, ...args);
        let body;
        try {
            body = format(this.formatMessage(message), ...args);
        }
        catch {
            // Fallback if format causes recursion
            body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
        }
        this.emit(LogsAPI.SeverityNumber.DEBUG, 'DEBUG', body);
    }
    info(message, ...args) {
        if (!this.shouldLog(LogsAPI.SeverityNumber.INFO)) {
            return;
        }
        this.logger?.info(message, ...args);
        let body;
        try {
            body = format(this.formatMessage(message), ...args);
        }
        catch {
            // Fallback if format causes recursion
            body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
        }
        this.emit(LogsAPI.SeverityNumber.INFO, 'INFO', body);
    }
    warn(message, ...args) {
        if (!this.shouldLog(LogsAPI.SeverityNumber.WARN)) {
            return;
        }
        this.logger?.warn(message, ...args);
        let body;
        try {
            body = format(this.formatMessage(message), ...args);
        }
        catch {
            // Fallback if format causes recursion
            body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
        }
        this.emit(LogsAPI.SeverityNumber.WARN, 'WARN', body);
    }
    error(message, ...args) {
        if (!this.shouldLog(LogsAPI.SeverityNumber.ERROR)) {
            return;
        }
        this.logger?.error(message, ...args);
        let body;
        try {
            body = format(this.formatMessage(message), ...args);
        }
        catch {
            // Fallback if format causes recursion
            body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
        }
        this.emit(LogsAPI.SeverityNumber.ERROR, 'ERROR', body);
    }
    fatal(message, ...args) {
        this.error(message, ...args);
        process.exit(1);
    }
    child(opts) {
        return new OtelLogger(!!this.logger, this.delegate, {
            ...(this.context ?? {}),
            ...opts,
        }, this.logLevel);
    }
}
/**
 * Creates a logger that integrates with OpenTelemetry
 *
 * @param useConsole - Whether to also log to the console
 * @param context - Additional context to include with log records
 * @returns A logger instance
 */
export function createLogger(useConsole, context, logLevel) {
    const delegate = LogsAPI.logs.getLogger('default', undefined, {
        scopeAttributes: context,
    });
    return new OtelLogger(useConsole, delegate, context, logLevel);
}
/**
 * Patches the global console object to integrate with OpenTelemetry logging
 *
 * @param attributes - Attributes to include with all console log records
 */
export function patchConsole(enabled, attributes, logLevel) {
    if (!enabled) {
        return;
    }
    const _patch = { ...__originalConsole };
    const delegate = createLogger(true, attributes, logLevel);
    // Patch individual console methods instead of reassigning the whole object
    _patch.log = (...args) => {
        delegate.info(args[0], ...args.slice(1));
    };
    _patch.error = (...args) => {
        delegate.error(args[0], ...args.slice(1));
    };
    _patch.warn = (...args) => {
        delegate.warn(args[0], ...args.slice(1));
    };
    _patch.debug = (...args) => {
        delegate.debug(args[0], ...args.slice(1));
    };
    _patch.info = (...args) => {
        delegate.info(args[0], ...args.slice(1));
    };
    _patch.dir = (...args) => {
        let msg = '';
        if (args.length === 1) {
            msg = format(args[0]);
        }
        else if (args.length >= 2) {
            msg = format(args[0], args[1]);
        }
        else {
            msg = safeStringify(args);
        }
        delegate.debug(msg);
    };
    _patch.dirxml = (...args) => {
        delegate.debug('dirxml:', ...args);
    };
    _patch.table = (...args) => {
        delegate.debug('table:', ...args);
    };
    _patch.trace = (...args) => {
        delegate.debug(args[0], ...args.slice(1));
    };
    _patch.group = (...args) => {
        delegate.debug('group:', ...args);
    };
    _patch.groupCollapsed = (...args) => {
        delegate.debug('groupCollapsed:', ...args);
    };
    _patch.groupEnd = () => {
        delegate.debug('groupEnd');
    };
    _patch.clear = () => {
        /* no-op */
    };
    _patch.count = (...args) => {
        delegate.debug('count:', ...args);
    };
    _patch.countReset = (...args) => {
        delegate.debug('countReset:', ...args);
    };
    _patch.assert = (condition, ...args) => {
        if (!condition) {
            delegate.error('assertion failed:', ...args);
        }
    };
    _patch.time = (...args) => {
        delegate.debug('time:', ...args);
    };
    _patch.timeLog = (...args) => {
        delegate.debug('timeLog:', ...args);
    };
    _patch.timeEnd = (...args) => {
        delegate.debug('timeEnd:', ...args);
    };
    _patch.profile = (...args) => {
        delegate.debug('profile:', ...args);
    };
    _patch.profileEnd = (...args) => {
        delegate.debug('profileEnd:', ...args);
    };
    // biome-ignore lint/suspicious/noGlobalAssign: intentionally replacing console with instrumented version
    console = globalThis.console = _patch;
}
//# sourceMappingURL=logger.js.map