"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Logger = void 0;
const log4js = require("log4js");
const util = require("util");
const readline = require("readline");
const stream = require("stream");
const marked = require("marked");
const _ = require("lodash");
const decorators_1 = require("../decorators");
const cli_layout_1 = require("./layouts/cli-layout");
const constants_1 = require("../../constants");
const yok_1 = require("../yok");
const TerminalRenderer = require("marked-terminal");
const chalk = require("chalk");
class Logger {
    constructor($config) {
        this.$config = $config;
        this.log4jsLogger = null;
        this.passwordRegex = /(password=).*?(['&,]|$)|(password["']?\s*:\s*["']).*?(["'])/i;
        this.passwordReplacement = "$1$3*******$2$4";
        this.defaultLogLevel = this.$config.DEBUG
            ? constants_1.LoggerLevel.TRACE
            : constants_1.LoggerLevel.INFO;
    }
    initialize(opts) {
        opts = opts || {};
        const { appenderOptions: appenderOpts, level } = opts;
        const appender = {
            type: "console",
            layout: {
                type: "messagePassThrough",
            },
        };
        if (appenderOpts) {
            _.merge(appender, appenderOpts);
        }
        const appenders = {
            out: appender,
        };
        const categories = {
            default: {
                appenders: ["out"],
                level: level || this.defaultLogLevel,
            },
        };
        log4js.configure({ appenders, categories });
        this.log4jsLogger = log4js.getLogger();
        if (level === constants_1.LoggerLevel.TRACE || level === constants_1.LoggerLevel.ALL) {
            this.warn(`The "${level}" log level might print some sensitive data like secrets or access tokens in request URLs. Be careful when you share this output.`, { wrapMessageWithBorders: true });
        }
    }
    initializeCliLogger(opts) {
        log4js.addLayout("cli", cli_layout_1.layout);
        this.initialize({
            appenderOptions: {
                type: constants_1.LoggerAppenders.cliAppender,
                layout: { type: "cli" },
            },
            level: opts.level || this.defaultLogLevel,
        });
    }
    getLevel() {
        this.initialize();
        return this.log4jsLogger.level.toString();
    }
    fatal(...args) {
        this.logMessage(args, constants_1.LoggerLevel.FATAL);
    }
    error(...args) {
        args.push({ [constants_1.LoggerConfigData.useStderr]: true });
        this.logMessage(args, constants_1.LoggerLevel.ERROR);
    }
    warn(...args) {
        this.logMessage(args, constants_1.LoggerLevel.WARN);
    }
    info(...args) {
        this.logMessage(args, constants_1.LoggerLevel.INFO);
    }
    debug(...args) {
        const encodedArgs = this.getPasswordEncodedArguments(args);
        this.logMessage(encodedArgs, constants_1.LoggerLevel.DEBUG);
    }
    trace(...args) {
        const encodedArgs = this.getPasswordEncodedArguments(args);
        this.logMessage(encodedArgs, constants_1.LoggerLevel.TRACE);
    }
    prepare(item) {
        if (typeof item === "undefined" || item === null) {
            return "[no content]";
        }
        if (typeof item === "string") {
            return item;
        }
        if (item instanceof stream.Readable) {
            return "[ReadableStream]";
        }
        if (item instanceof Buffer) {
            return "[Buffer]";
        }
        return JSON.stringify(item);
    }
    printMarkdown(...args) {
        const opts = {
            unescape: true,
            link: chalk.red,
            strong: chalk.green.bold,
            firstHeading: chalk.blue.bold,
            tableOptions: {
                chars: { mid: "", "left-mid": "", "mid-mid": "", "right-mid": "" },
                style: {
                    "padding-left": 1,
                    "padding-right": 1,
                    head: ["green", "bold"],
                    border: ["grey"],
                    compact: false,
                },
            },
        };
        marked.setOptions({ renderer: new TerminalRenderer(opts) });
        const formattedMessage = marked(util.format.apply(null, args));
        this.info(formattedMessage, { [constants_1.LoggerConfigData.skipNewLine]: true });
    }
    isVerbose() {
        return log4js.levels.DEBUG.isGreaterThanOrEqualTo(this.getLevel());
    }
    clearScreen() {
        const repeatCount = process.stdout.rows - 2;
        const blank = repeatCount > 0 ? "\n".repeat(repeatCount) : "";
        console.log(blank);
        readline.cursorTo(process.stdout, 0, 0);
        readline.clearScreenDown(process.stdout);
    }
    logMessage(inputData, logMethod) {
        this.initialize();
        const logOpts = this.getLogOptionsForMessage(inputData);
        const data = logOpts.data;
        delete logOpts.data;
        for (const prop in logOpts) {
            this.log4jsLogger.addContext(prop, logOpts[prop]);
        }
        this.log4jsLogger[logMethod.toLowerCase()].apply(this.log4jsLogger, data);
        for (const prop in logOpts) {
            this.log4jsLogger.removeContext(prop);
        }
    }
    getLogOptionsForMessage(data) {
        const loggerOptionKeys = _.keys(constants_1.LoggerConfigData);
        const dataToCheck = data.filter((el) => {
            if (!!el &&
                typeof el === "object" &&
                el.hasOwnProperty &&
                typeof el.hasOwnProperty === "function") {
                for (const key of loggerOptionKeys) {
                    if (el.hasOwnProperty(key)) {
                        return true;
                    }
                }
            }
            return false;
        });
        const result = {
            data: _.difference(data, dataToCheck),
        };
        for (const element of dataToCheck) {
            if (loggerOptionKeys.length === 0) {
                break;
            }
            const remainingOpts = _.cloneDeep(loggerOptionKeys);
            for (const prop of remainingOpts) {
                const hasProp = element && element.hasOwnProperty(prop);
                if (hasProp) {
                    loggerOptionKeys.splice(loggerOptionKeys.indexOf(prop), 1);
                    result[prop] = element[prop];
                }
            }
        }
        return result;
    }
    getPasswordEncodedArguments(args) {
        return _.map(args, (argument) => {
            if (typeof argument === "string" && !!argument.match(/password/i)) {
                argument = argument.replace(this.passwordRegex, this.passwordReplacement);
            }
            return argument;
        });
    }
}
__decorate([
    (0, decorators_1.cache)()
], Logger.prototype, "initialize", null);
exports.Logger = Logger;
yok_1.injector.register("logger", Logger);
