"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.consoleLog = void 0;
exports.consoleHint = consoleHint;
const events_1 = __importDefault(require("./events"));
const cross_1 = require("./cross");
const fs_1 = require("fs");
const argv_1 = require("./argv");
exports.consoleLog = [];
const originalConsoleLog = console.log;
const f = argv_1.argv.consoleFile ? (0, fs_1.createWriteStream)(argv_1.argv.consoleFile, { flags: 'a', encoding: 'utf8' }) : null;
let terminalOutputBroken = false;
for (const stream of [process.stdout, process.stderr])
    stream.on('error', err => {
        if (!isBrokenTerminalOutput(err))
            throw err;
        // after the terminal/pipe is gone, further console writes would just re-emit the same process-level error
        terminalOutputBroken = true;
    });
for (const k of ['log', 'warn', 'error', 'debug']) {
    const original = console[k];
    console[k] = (...args) => {
        const ts = new Date();
        if (k === 'debug')
            args.unshift('DBG');
        else {
            const msg = safeJoin(args); // if args contains a symbol, join will throw
            const rec = { ts, k, msg };
            exports.consoleLog.push(rec);
            if (exports.consoleLog.length > 100_000) // limit to avoid infinite space
                exports.consoleLog.splice(0, 1_000);
            events_1.default.emit('console', rec);
            f?.write(`${(0, cross_1.formatTimestamp)(ts)} [${k}] ${msg}\n`);
            if (k !== 'log')
                args.unshift('!');
        }
        if (!terminalOutputBroken) {
            try {
                return original((0, cross_1.formatTime)(ts), ...args);
            } // bundled nodejs doesn't have locales (and apparently uses en-US)
            catch (err) {
                if (!isBrokenTerminalOutput(err))
                    throw err;
                terminalOutputBroken = true;
            }
        }
    };
    Object.assign(console[k], { original });
}
const over = console.log;
for (const k of ['table']) {
    const original = console[k];
    console[k] = (...args) => {
        console.log = originalConsoleLog;
        // @ts-ignore
        try {
            return original(...args);
        }
        finally {
            console.log = over;
        }
    };
}
function safeJoin(a) {
    try {
        return a.join(' ');
    }
    catch {
        return a.map(x => {
            if (x == null)
                return '';
            try {
                return String(x);
            }
            catch {
                if (Array.isArray(x))
                    return `[${safeJoin(x)}]`;
                try {
                    return JSON.stringify(x);
                }
                catch {
                    return 'N/A';
                }
            }
        }).join(' ');
    }
}
function isBrokenTerminalOutput(err) {
    const code = err?.code;
    return code === 'EPIPE' || code === 'EIO'
        || code === 'ERR_STREAM_DESTROYED' || code === 'ERR_STREAM_WRITE_AFTER_END';
}
function consoleHint(msg) {
    console.log("HINT: " + msg);
}
