import { APICallError } from "ai";
import { sanitizeLogMessage, sanitizeLogObject, redactStackTrace, safeJsonStringify, } from "./log-sanitizer.js";
const logListeners = new Set();
export function registerLogListener(listener) {
    logListeners.add(listener);
    return () => {
        logListeners.delete(listener);
    };
}
function notifyListeners(level, messages, meta) {
    if (logListeners.size === 0) {
        return;
    }
    const entry = {
        level,
        messages,
        meta,
        timestamp: Date.now(),
    };
    for (const listener of logListeners) {
        try {
            listener(entry);
        }
        catch (err) {
            console.warn("Error in log listener", err);
        }
    }
}
// Marker to identify wrapped loggers and prevent double-wrapping
const WRAPPED_MARKER = Symbol("wrappedLogger");
// Cache to return the same wrapped logger for the same input
const wrappedLoggerCache = new WeakMap();
/**
 * Sanitize a single log argument (secrets only).
 */
function sanitizeArg(arg) {
    if (typeof arg === "string") {
        return sanitizeLogMessage(arg);
    }
    if (typeof arg === "object" && arg !== null) {
        return sanitizeLogObject(arg);
    }
    return arg;
}
/**
 * Sanitize all log arguments (secrets only).
 */
function sanitizeArgs(args) {
    return args.map(sanitizeArg);
}
/**
 * Sanitize error metadata (secrets only, preserves stack traces).
 */
function sanitizeErrorMeta(meta) {
    if (!meta?.error)
        return meta;
    return {
        error: {
            kind: meta.error.kind,
            message: sanitizeLogMessage(meta.error.message),
            // redactStackTrace now preserves the stack, only sanitizes secrets within
            stack: meta.error.stack ? redactStackTrace(meta.error.stack) : undefined,
        },
    };
}
/**
 * Wraps a logger to always sanitize secrets from log messages.
 * Secrets (API keys, tokens, JWTs) are redacted.
 * Debugging info (emails, paths, stack traces) is preserved.
 */
function wrapLogger(logger) {
    // Already wrapped - return as-is
    if (logger[WRAPPED_MARKER]) {
        return logger;
    }
    // Check cache for previously wrapped version
    const cached = wrappedLoggerCache.get(logger);
    if (cached) {
        return cached;
    }
    // Always sanitize secrets - they should NEVER appear in logs
    const wrapped = {
        debug: (...messages) => {
            const sanitized = sanitizeArgs(messages);
            notifyListeners("debug", sanitized);
            logger.debug(...sanitized);
        },
        info: (...messages) => {
            const sanitized = sanitizeArgs(messages);
            notifyListeners("info", sanitized);
            logger.info(...sanitized);
        },
        warn: (...messages) => {
            const sanitized = sanitizeArgs(messages);
            notifyListeners("warn", sanitized);
            logger.warn(...sanitized);
        },
        error: (message, meta) => {
            const sanitizedMessage = sanitizeLogMessage(message);
            const sanitizedMeta = sanitizeErrorMeta(meta);
            notifyListeners("error", [sanitizedMessage], sanitizedMeta);
            logger.error(sanitizedMessage, sanitizedMeta);
        },
    };
    // Mark the WRAPPED logger (not the input) to prevent re-wrapping
    wrapped[WRAPPED_MARKER] = true;
    wrappedLoggerCache.set(logger, wrapped);
    return wrapped;
}
// Default logger instance for backwards compatibility
let _defaultLogger;
/** Cached proxy returned by getLogger(); invalidated on setDefaultLogger. */
let _cachedProxy;
export function setDefaultLogger(logger) {
    _defaultLogger = wrapLogger(logger);
    _cachedProxy = undefined; // Invalidate cached proxy
}
const consoleLogger = wrapLogger({
    debug: (...messages) => console.debug(...messages),
    info: (...messages) => console.info(...messages),
    warn: (...messages) => console.warn(...messages),
    error: (message, meta) => console.error(message, meta),
});
export function getLogger() {
    if (_cachedProxy)
        return _cachedProxy;
    _cachedProxy = {
        debug: (...messages) => (_defaultLogger ?? consoleLogger).debug(...messages),
        info: (...messages) => (_defaultLogger ?? consoleLogger).info(...messages),
        warn: (...messages) => (_defaultLogger ?? consoleLogger).warn(...messages),
        error: (message, meta) => (_defaultLogger ?? consoleLogger).error(message, meta),
    };
    return _cachedProxy;
}
/**
 * Extracts error metadata from an error, sanitizing secrets.
 * Preserves full stack traces and debugging info for troubleshooting.
 */
export function getErrorMeta(error) {
    if (APICallError.isInstance(error)) {
        return {
            error: {
                kind: error.name,
                message: safeJsonStringify({
                    message: sanitizeLogMessage(error.message),
                    responseBody: error.responseBody
                        ? sanitizeLogObject(error.responseBody)
                        : undefined,
                    statusCode: error.statusCode,
                    url: error.url ? sanitizeLogMessage(error.url) : undefined,
                }),
                // Preserve full stack trace, only sanitize secrets within
                stack: error.stack ? redactStackTrace(error.stack) : undefined,
            },
        };
    }
    if (error instanceof Error) {
        return {
            error: {
                kind: error.name,
                message: sanitizeLogMessage(error.message),
                // Preserve full stack trace, only sanitize secrets within
                stack: error.stack ? redactStackTrace(error.stack) : undefined,
            },
        };
    }
    return {
        error: {
            kind: "Unknown Error",
            message: safeJsonStringify(error),
        },
    };
}
/**
 * Creates a logger with automatic prefix application.
 *
 * @param prefix - Prefix to prepend to all log messages
 * @param verbose - If false, debug logs are suppressed (default: true)
 * @returns Logger instance with prefix baked in
 */
export function getPrefixedLogger(prefix, verbose = true) {
    const baseLogger = getLogger();
    return {
        debug: (...messages) => {
            if (verbose) {
                baseLogger.debug(prefix, ...messages);
            }
        },
        info: (...messages) => baseLogger.info(prefix, ...messages),
        warn: (...messages) => baseLogger.warn(prefix, ...messages),
        error: (message, meta) => baseLogger.error(`${prefix} ${message}`, meta),
    };
}
//# sourceMappingURL=logger.js.map