const chalk = require('chalk');

/**
 * @typedef {object} LogMessage
 * @property {string} text
 * @property {'info' | 'warn' | 'error'} type
 **/

/**
 * @typedef {'default' | 'green' | 'red'} LogColor
 **/

class Log {
    /**
     * Determine if we are in test mode.
     */
    static testing = false;

    /**
     * All logged messages.
     *
     * @type {string[]}
     */
    static fakedLogs = [];

    /**
     * Log basic info to the console.
     *
     * @param  {string} message
     * @param  {LogColor} color
     */
    static info(message, color = 'default') {
        if (Log.testing) {
            Log.fakedLogs.push(message);

            return;
        }

        console.log(Log.colors()[color], message);

        Log.reset();
    }

    /**
     *
     * @param {LogMessage} message
     */
    static message(message) {
        if (Log.testing) {
            Log.fakedLogs.push(message.text);

            return;
        }

        /** @type {string} */
        let prefix = '';

        if (message.type === 'info') {
            prefix = ' INFO ';
        } else if (message.type === 'warn') {
            prefix = ' WARN ';
        } else if (message.type === 'error') {
            prefix = ' ERR ';
        }

        const line = message.text.replace(/\n/g, '\n' + ' '.repeat(prefix.length + 1));

        if (message.type === 'info') {
            console.warn(`${chalk.bgBlue.white(prefix)} ${chalk.white(line)}`);
        } else if (message.type === 'warn') {
            console.warn(`${chalk.bgYellow.black(prefix)} ${chalk.yellow(line)}`);
        } else if (message.type === 'error') {
            console.warn(`${chalk.bgRed.white(prefix)} ${chalk.red(line)}`);
        }
    }

    /**
     * Log feedback info to the console.
     *
     * @param  {string} message
     * @param  {LogColor} color
     */
    static feedback(message, color = 'green') {
        Log.line('\t' + message, color);
    }

    /**
     * Log error info to the console.
     *
     * @param  {string} message
     * @param  {LogColor} color
     */
    static error(message, color = 'red') {
        Log.line(message, color);
    }

    /**
     * Log a new line of info to the console.
     *
     * @param  {string} message
     * @param  {LogColor} color
     */
    static line(message, color = 'default') {
        Log.info(message, color);
    }

    /**
     * Reset the default color for future console.logs.
     */
    static reset() {
        console.log(Log.colors()['default'], '');
    }

    /**
     * Enter testing mode.
     */
    static fake() {
        Log.testing = true;
    }

    /**
     * Exit testing mode.
     */
    static restore() {
        Log.testing = false;
        Log.fakedLogs = [];
    }

    /**
     * Determine if the given message was logged.
     *
     * @param  {string|string[]} messages
     */
    static received(messages) {
        messages = Array.isArray(messages) ? messages : [messages];

        let result = messages.every(message =>
            this.fakedLogs.some(log => log.includes(message))
        );

        this.restore();

        return result;
    }

    /**
     * The colors lookup table.
     */
    static colors() {
        return {
            default: '\x1b[0m',
            green: '\x1b[32m',
            red: '\x1b[31m'
        };
    }
}

module.exports = Log;
