const getEnv = require('./getEnv');
const maskSensitiveFields = require('./maskSensitiveFields');
const env = getEnv();

const MAX_MESSAGE_LENGTH = 10240;
const ConsoleTypes = ['debug', 'error', 'info', 'log', 'warn'];

const LevelMap = {
    log: 'info',
};

const StdOut = ConsoleTypes.reduce((acc, type) => {
    // eslint-disable-next-line no-console
    acc[type] = console[type].bind(console);

    return acc;
}, {});

const EscapeSequence = {
    error: '\x1b[31m', // red
    warn: '\x1b[33m', // yellow
    stop: '\x1b[0m',
};

function color(s, type) {
    return EscapeSequence[type] ? `${EscapeSequence[type]}${s}${EscapeSequence.stop}` : s;
}

function dim(s) {
    return `\x1b[2m${s}\x1b[0m`;
}

function log(type, ...args) {
    let message;
    let options;

    if (typeof args[0] === 'string') {
        message = args.shift();
    }

    if (args.length !== 0) {
        options = maskSensitiveFields(args.length === 1 ? args[0] : args);
    }

    if (env === 'development') {
        StdOut[type]();
        StdOut[type](color(dim(new Date().toISOString()), type));

        if (message !== undefined) {
            StdOut[type](color(message.substring(0, MAX_MESSAGE_LENGTH), type));
        }

        if (options) {
            if (options.stack) {
                StdOut[type](color(dim(options.stack), type));
            }

            StdOut[type](color(dim(JSON.stringify(options)), type));
        }
    } else {
        StdOut[type](JSON.stringify({
            level: LevelMap[type] || type,
            message,
            '@fields': options,
        }));
    }
}

ConsoleTypes.forEach(type => {
    // eslint-disable-next-line no-console
    console[type] = (...args) => log(type, ...args);
});
