import * as tsLogger from "tslog";
import { types } from "util";
import { Level, LOGGER_GROUP_KEY, SERVICE_KEY, STACKTRACE_KEY, SUBJECT_KEY, TRACING_KEY } from "./types";
import { v4 as uuidv4 } from 'uuid';
export let main;
// class to wrap all non-native errors in a safe error
class ErrorWrapper extends Error {
    constructor(err) {
        super();
        this.message = err.message;
        this.name = err.name;
        this.stack = err.stack;
    }
}
export const NewMainLogger = (settings) => {
    main = newLogger(settings);
};
const loggerSettingsToTslogSettings = (settings) => {
    return settings;
};
const loggerSettingsToTslogObj = (settings) => {
    const obj = {};
    if (settings === null || settings === void 0 ? void 0 : settings.tracingId) {
        obj[TRACING_KEY] = settings === null || settings === void 0 ? void 0 : settings.tracingId;
    }
    if (settings === null || settings === void 0 ? void 0 : settings.subject) {
        obj[SUBJECT_KEY] = settings === null || settings === void 0 ? void 0 : settings.subject;
    }
    if (settings === null || settings === void 0 ? void 0 : settings.service) {
        obj[SERVICE_KEY] = settings === null || settings === void 0 ? void 0 : settings.service;
    }
    return Object.assign(Object.assign({}, obj), getGroupId());
};
const getLoggerLevel = (settings) => {
    switch (settings === null || settings === void 0 ? void 0 : settings.level) {
        case "DEBUG":
            return Level.Debug;
        case "WARN":
            return Level.Warn;
        case "ERROR":
            return Level.Error;
    }
    return Level.Info;
};
const getGroupId = () => {
    return { [LOGGER_GROUP_KEY]: uuidv4() };
};
export const newLogger = (settings) => {
    const s = loggerSettingsToTslogSettings(settings);
    const obj = loggerSettingsToTslogObj(settings);
    return new Logger(getLoggerLevel(settings), s, obj);
};
class Logger extends tsLogger.Logger {
    constructor(level, settings, logObj) {
        const expandedSettings = Object.assign(Object.assign({}, settings), { hideLogPositionForProduction: true });
        expandedSettings.type = "json";
        expandedSettings.minLevel = level;
        expandedSettings.overwrite = {
            addMeta: (logObj, logLevelId, logLevelName) => {
                return Object.assign(Object.assign({}, logObj), this.getMeta(logLevelId, logLevelName, this.settings.name, this.settings.parentNames));
            },
            toLogObj: (args, clonesLogObj) => {
                let logObj = {};
                const thisLogObj = this.obj;
                for (const key of Object.keys(thisLogObj)) {
                    if (this.isError(thisLogObj[key])) {
                        logObj[key] = this.toErrorObject(thisLogObj[key]);
                    }
                    else {
                        logObj[key] = thisLogObj[key];
                    }
                }
                for (let i = 0; i < args.length; i++) {
                    // after 3rd param we just add index-2
                    if (i > 2) {
                        logObj[(i - 2).toString()] = args[i];
                    }
                    // error, message, fields
                    // error, message
                    // message, fields
                    // message
                    if (this.isError(args[i])) {
                        logObj["error"] = this.toErrorObject(args[i]);
                    }
                    else if (args[i] instanceof String || typeof args[i] === "string") {
                        logObj["message"] = args[i];
                    }
                    else if (args[i] instanceof Object) {
                        logObj = Object.assign(Object.assign({}, logObj), args[i]);
                    }
                    else {
                        logObj["message"] = args[i];
                    }
                }
                // convert the log attributes to a T and return it
                return logObj;
            },
        };
        super(expandedSettings, logObj);
        this.obj = logObj || {};
    }
    getChildLogger(name) {
        // we concat logger names directly here
        // another options is to inject [this.settings.parentNames, this.settings.name] in settings
        // to preserve tslog behavior
        let n = `${this.settings.name}`;
        if (name === null || name === void 0 ? void 0 : name.length) {
            n = `${this.settings.name}.${name}`;
        }
        return new Logger(this.settings.minLevel, Object.assign(Object.assign(Object.assign({}, this.settings), { name: n }), getGroupId()));
    }
    withError(err) {
        if (!this.isError(err)) {
            return this;
        }
        const _err = this.wrapError(err);
        return new Logger(this.settings.minLevel, this.settings, Object.assign(Object.assign({}, this.obj), { error: _err }));
    }
    with(args) {
        return new Logger(this.settings.minLevel, this.settings, Object.assign(Object.assign({}, this.obj), args));
    }
    toErrorObject(error) {
        var _a;
        return {
            nativeError: error,
            name: (_a = error.name) !== null && _a !== void 0 ? _a : "Error",
            message: error.message,
            [STACKTRACE_KEY]: error.stack,
        };
    }
    isError(e) {
        // An error could be an instance of Error while not being a native error
        // or could be from a different realm and not be instance of Error but still
        // be a native error.
        if ((types === null || types === void 0 ? void 0 : types.isNativeError) != null && types.isNativeError(e)) {
            return true;
        }
        return e instanceof Error;
    }
    wrapError(e) {
        if ((types === null || types === void 0 ? void 0 : types.isNativeError) != null && types.isNativeError(e)) {
            return e;
        }
        return new ErrorWrapper(e);
    }
    getMeta(logLevelId, logLevelName, name, parentNames) {
        var _a;
        // faster than spread operator
        return Object.assign({}, {
            runtime: ![typeof window, typeof document].includes("undefined") ? "Browser" : "Generic",
            browser: (_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis["navigator"]) === null || _a === void 0 ? void 0 : _a.userAgent,
        }, {
            name,
            parentNames,
            date: new Date(),
            logLevelId,
            logLevelName,
        });
    }
}
export default Logger;
//# sourceMappingURL=logger.js.map