/**
 * Phase 11 — centralized logger.
 *
 * Single import surface for every log call site:
 *
 *   import { log } from '../../utils/log.js';
 *   log.info('starting up');
 *   log.warn('something looks off:', err);
 *
 * All output flows through `sanitize()` from the server middleware so
 * registry-listed secrets, prefix-shaped tokens, URL credentials, and
 * JSON-quoted secret fields are redacted before reaching stdout/stderr.
 *
 * Modes:
 *   - Production (`NODE_ENV === 'production'` or `RAILWAY_ENVIRONMENT` set):
 *     sanitize() is always applied.
 *   - Development (anything else): raw passthrough — easier to debug
 *     with real values visible in your own terminal.
 *   - Escape hatch (`STUDIOGRAPH_LOG_RAW=1`): force raw mode regardless of
 *     environment. Useful when troubleshooting redaction itself.
 *
 * Why expose `log.*` instead of patching console.* directly here? Two
 * reasons. First, the console patch (in serve.ts) is only safe to apply
 * in production — local dev needs real console for debugging. Second,
 * the explicit `log.*` import surface is what the `no-raw-console` lint
 * rule keys off of: any direct `console.*` call outside this file is a
 * lint warning telling the author to migrate.
 */
import { sanitize } from '../server/middleware/sanitize.js';
/**
 * Decide whether the current process should redact log output. Resolved
 * dynamically on every call so tests can flip env vars between cases.
 */
function shouldRedact() {
    if (process.env.STUDIOGRAPH_LOG_RAW === '1')
        return false;
    return (process.env.NODE_ENV === 'production' ||
        !!process.env.RAILWAY_ENVIRONMENT);
}
/**
 * Sanitize a console-style arg list. Strings flow through sanitize();
 * objects are JSON.stringified, sanitized, and JSON.parsed back into a
 * lightly-modified shape so downstream consumers (Fastify logger, error
 * serializers) can still walk them. Non-serializable args fall back to
 * String() coercion.
 */
function maybeSanitizeArgs(args) {
    if (!shouldRedact())
        return args;
    return args.map((a) => {
        if (typeof a === 'string')
            return sanitize(a);
        if (a instanceof Error) {
            // Construct a new Error with sanitized message + stack so the
            // shape survives downstream `instanceof Error` checks. Note: we
            // can't mutate the original because Error.message is read-only
            // on some V8 builds.
            const e = new Error(sanitize(a.message));
            if (a.stack)
                e.stack = sanitize(a.stack);
            // Preserve any additional properties (e.g. `code`).
            Object.assign(e, { ...a });
            return e;
        }
        if (a && typeof a === 'object') {
            try {
                return JSON.parse(sanitize(JSON.stringify(a)));
            }
            catch {
                return sanitize(String(a));
            }
        }
        return a;
    });
}
/**
 * The exported logger. Mirrors console's API one-to-one so migration is
 * a search-and-replace: `console.X(...) → log.X(...)`.
 *
 * `log.debug` exists for symmetry; it's silenced in production unless
 * `STUDIOGRAPH_VERBOSE=1`.
 */
export const log = {
    info: (...args) => {
        // eslint-disable-next-line no-console
        console.log(...maybeSanitizeArgs(args));
    },
    warn: (...args) => {
        // eslint-disable-next-line no-console
        console.warn(...maybeSanitizeArgs(args));
    },
    error: (...args) => {
        // eslint-disable-next-line no-console
        console.error(...maybeSanitizeArgs(args));
    },
    debug: (...args) => {
        if (shouldRedact() && process.env.STUDIOGRAPH_VERBOSE !== '1')
            return;
        // eslint-disable-next-line no-console
        console.debug(...maybeSanitizeArgs(args));
    },
};
/**
 * Idempotent installer for the production console patch. Wraps the four
 * console methods so any third-party (or yet-to-migrate) callsite that
 * still hits `console.*` directly also runs through sanitize() in prod.
 *
 * Called once at boot from `serve.ts` after the master key is loaded
 * (waiting until after master-key load avoids redacting the master-key
 * info line during normal boot). Safe to call multiple times — second
 * call no-ops because the patch records itself on a process-level
 * Symbol.
 */
const CONSOLE_PATCHED = Symbol.for('studiograph.console.patched');
export function installConsolePatch() {
    if (!shouldRedact())
        return;
    const g = globalThis;
    if (g[CONSOLE_PATCHED])
        return;
    g[CONSOLE_PATCHED] = true;
    // Capture the originals so the patch can hand the sanitized args back
    // to the underlying writer.
    const orig = {
        log: console.log.bind(console),
        info: console.info.bind(console),
        warn: console.warn.bind(console),
        error: console.error.bind(console),
        debug: console.debug.bind(console),
    };
    console.log = (...args) => orig.log(...maybeSanitizeArgs(args));
    console.info = (...args) => orig.info(...maybeSanitizeArgs(args));
    console.warn = (...args) => orig.warn(...maybeSanitizeArgs(args));
    console.error = (...args) => orig.error(...maybeSanitizeArgs(args));
    console.debug = (...args) => orig.debug(...maybeSanitizeArgs(args));
}
//# sourceMappingURL=log.js.map