"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MongoLogWriter = void 0;
exports.mongoLogId = mongoLogId;
const bson_1 = require("bson");
const stream_1 = require("stream");
const util_1 = require("util");
const kFlushDummy = Symbol('kFlushDummy');
function mongoLogId(id) {
    return { __value: id };
}
function validateLogEntry(info) {
    if (typeof info.s !== 'string') {
        return new TypeError('Cannot log messages without a severity field');
    }
    if (typeof info.c !== 'string') {
        return new TypeError('Cannot log messages without a component field');
    }
    if (typeof info.id?.__value !== 'number') {
        return new TypeError('Cannot log messages without an id field');
    }
    if (typeof info.ctx !== 'string') {
        return new TypeError('Cannot log messages without a context field');
    }
    if (typeof info.msg !== 'string') {
        return new TypeError('Cannot log messages without a message field');
    }
    return null;
}
class MongoLogWriter extends stream_1.Writable {
    _logId;
    _logFilePath;
    _target;
    _now;
    constructor(logId, logFilePath, target, now) {
        super({ objectMode: true });
        this._logId = logId;
        this._logFilePath = logFilePath;
        this._target = target;
        this._now = now ?? (() => new Date());
    }
    get logId() {
        return this._logId;
    }
    get logFilePath() {
        return this._logFilePath;
    }
    get target() {
        return this._target;
    }
    _write(info, encoding, callback) {
        if (info === kFlushDummy) {
            this._target.write('', callback);
            return;
        }
        const validationError = validateLogEntry(info);
        if (validationError) {
            callback(validationError);
            return;
        }
        const fullInfo = {
            t: info.t ?? this._now(),
            s: info.s,
            c: info.c,
            id: info.id.__value,
            ctx: info.ctx,
            msg: info.msg,
        };
        if (info.attr) {
            if (Object.prototype.toString.call(info.attr) === '[object Error]') {
                fullInfo.attr = {
                    stack: info.attr.stack,
                    name: info.attr.name,
                    message: info.attr.message,
                    code: info.attr.code,
                    ...info.attr,
                };
            }
            else {
                fullInfo.attr = info.attr;
            }
        }
        this.emit('log', fullInfo);
        try {
            bson_1.EJSON.stringify(fullInfo.attr);
        }
        catch {
            try {
                const v8 = require('v8');
                const cloned = v8.deserialize(v8.serialize(fullInfo.attr));
                bson_1.EJSON.stringify(cloned);
                fullInfo.attr = cloned;
            }
            catch {
                try {
                    const cloned = JSON.parse(JSON.stringify(fullInfo.attr));
                    bson_1.EJSON.stringify(cloned);
                    fullInfo.attr = cloned;
                }
                catch {
                    fullInfo.attr = { _inspected: (0, util_1.inspect)(fullInfo.attr) };
                }
            }
        }
        this._target.write(bson_1.EJSON.stringify(fullInfo, { relaxed: true }) + '\n', callback);
    }
    _final(callback) {
        this._target.end(callback);
    }
    async flush() {
        await new Promise((resolve) => this.write(kFlushDummy, resolve));
    }
    info(component, id, context, message, attr) {
        const logEntry = {
            s: 'I',
            c: component,
            id: id,
            ctx: context,
            msg: message,
            attr: attr,
        };
        this.write(logEntry);
    }
    warn(component, id, context, message, attr) {
        const logEntry = {
            s: 'W',
            c: component,
            id: id,
            ctx: context,
            msg: message,
            attr: attr,
        };
        this.write(logEntry);
    }
    error(component, id, context, message, attr) {
        const logEntry = {
            s: 'E',
            c: component,
            id: id,
            ctx: context,
            msg: message,
            attr: attr,
        };
        this.write(logEntry);
    }
    fatal(component, id, context, message, attr) {
        const logEntry = {
            s: 'F',
            c: component,
            id: id,
            ctx: context,
            msg: message,
            attr: attr,
        };
        this.write(logEntry);
    }
    debug(component, id, context, message, attr, level = 1) {
        const logEntry = {
            s: `D${level}`,
            c: component,
            id: id,
            ctx: context,
            msg: message,
            attr: attr,
        };
        this.write(logEntry);
    }
    bindComponent(component) {
        return {
            unbound: this,
            component: component,
            write: (entry, cb) => this.write({ c: component, ...entry }, cb),
            info: this.info.bind(this, component),
            warn: this.warn.bind(this, component),
            error: this.error.bind(this, component),
            fatal: this.fatal.bind(this, component),
            debug: this.debug.bind(this, component),
        };
    }
    mongoLogId = mongoLogId;
}
exports.MongoLogWriter = MongoLogWriter;
//# sourceMappingURL=mongo-log-writer.js.map