const _ = require('lodash');
const winston = require('winston');
const moment = require('moment');
var colors = require('colors'); // eslint-disable-line
const config = require('../config');
const getLoggerOptions = require('./getLoggerOptions');

const LOG_LEVELS = ['error', 'warn', 'info', 'verbose', 'debug', 'silly'];

// Levels - { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }
const isDev = config.app.env === 'development';
const isUITest = config.app.env === 'ui-test';

const STRINGIFIED_FIELDS = [
    'body', 'options.body', 'err',
];

const SENSITIVE_FIELDS = [
    'headers.Authorization',
    'headers.X-Ya-Service-Ticket',
    'headers.X-Ya-User-Ticket',
    'options.headers.Authorization',
    'options.headers.X-Ya-Service-Ticket',
    'options.headers.X-Ya-User-Ticket',
    'options.qs.sessionid',
    'options.qs.sslsessionid',
    'qs.sessionid',
    'qs.sslsessionid',
    'body.password',
    'body.user_ticket',
    'body.blackbox.ticket',
    'body.directory-api.ticket',
    'body.gendarme.ticket',
    'body.dns.ticket',
    'body.setter.ticket',
    'body.fouras.ticket',
    'body.tvm_tickets.blackbox',
    'body.tvm_tickets.directory_api',
    'body.tvm_tickets.gendarme',
    'body.tvm_tickets.dns',
    'body.tvm_tickets.setter',
    'body.tvm_tickets.fouras',
    'options.body.password',
    'err.request.headers.Authorization',
    'err.request.headers.X-Ya-Service-Ticket',
    'err.request.headers.X-Ya-User-Ticket',
];

function maskSensitive(data) {
    const parsedFields = [];

    STRINGIFIED_FIELDS.forEach(key => {
        const value = _.get(data, key);

        if (typeof value === 'string') {
            try {
                _.set(data, key, JSON.parse(value));
                parsedFields.push(key);
            } catch (e) {
                // empty
            }
        }
    });

    SENSITIVE_FIELDS.forEach(key => {
        if (_.get(data, key) !== undefined) {
            _.set(data, key, '***');
        }
    });

    parsedFields.forEach(key => {
        const value = _.get(data, key);

        if (value !== undefined) {
            _.set(data, key, JSON.stringify(value));
        }
    });

    return data;
}

/**
 * Получение текущей даты в формате - "[13/04/2016:04:10:18 +0300]"
 * @returns {String}
 */
function getCurrentDate() {
    return `[${moment(new Date()).format('DD/MM/YYYY:hh:mm:ss ZZ')}]`;
}

/**
 * Генерирует строку с сообщением для лога
 * Если прод то строка будет без форматирования
 * @param {Object} options
 * @returns {String}
 */
function getMessage(options) {
    let meta = maskSensitive(options.meta);

    if (isDev || isUITest) {
        const date = getCurrentDate();
        const devMeta = _.isEmpty(meta) ? '' : JSON.stringify(meta);
        const devMessage = options.message || '';

        return `${date.grey} ${options.level.yellow} ${devMessage.green} ${devMeta.white}`;
    }

    meta = _.extend(meta, {
        level: _.get(options, 'level'),
        version: `${config.app.version}@${config.app.env}`,
    });

    // logger-friendly формат
    return JSON.stringify({
        '@message': `[${options.level}] ${options.message}`,
        '@fields': meta,
    });
}

const winstonLogger = new winston.Logger({
    level: config.app.logLevel,
    transports: [
        new winston.transports.Console({
            formatter: getMessage,
            // Без этого флага winston пишет дебаг логи в stderr
            debugStdout: true,
        }),
    ],
    // By default, winston will exit after logging an uncaughtException.
    // if this is not the behavior you want, set exitOnError = false
    exitOnError: false,
});

/**
 * Обертка для логирования данных
 * @param {String} logLevel
 * @param {String} message
 * @param {Request} req
 * @param {Object} data
 */
function log(logLevel, message, req, data) {
    winstonLogger.log(logLevel, message, req ? getLoggerOptions(req, data) : '');
}

const logger = {};

// Levels: error, warn, info, verbose, debug, silly
LOG_LEVELS.forEach(level => {
    logger[level] = log.bind(null, level);
});

module.exports = logger;
