/**
 * create-logger.js: Logger factory for winston logger instances.
 *
 * (C) 2010 Charlie Robbins
 * MIT LICENCE
 */

'use strict';

const { LEVEL } = require('triple-beam');
const config = require('./config');
const Logger = require('./logger');
const debug = require('diagnostics')('winston:create-logger');

/**
 * DerivedLogger to attach the logs level methods.
 * @type {DerivedLogger}
 * @extends {Logger}
 */
class DerivedLogger extends Logger {
  /**
   * Create a new class derived logger for which the levels can be attached to
   * the prototype of. This is a V8 optimization that is well know to increase
   * performance of prototype functions.
   * @param {!Object} options - Options for the created logger.
   */
  constructor(options) {
    super(options);
    this._setupLevels();
  }

  /**
   * Create the log level methods for the derived logger.
   * @returns {undefined}
   * @private
   */
  _setupLevels() {
    Object.keys(this.levels).forEach(level => {
      debug('Define prototype method for "%s"', level);
      if (level === 'log') {
        // eslint-disable-next-line no-console
        console.warn('Level "log" not defined: conflicts with the method "log". Use a different level name.');
        return;
      }

      // Define prototype methods for each log level
      // e.g. logger.log('info', msg) <––> logger.info(msg)
      this[level] = (...args) => {
        // Optimize the hot-path which is the single object.
        if (args.length === 1) {
          const [msg] = args;
          const info = msg && msg.message && msg || { message: msg };
          info.level = info[LEVEL] = level;
          this.write(info);
          return this;
        }

        // Otherwise build argument list which could potentially conform to
        // either:
        // . v3 API: log(obj)
        // 2. v1/v2 API: log(level, msg, ... [string interpolate], [{metadata}], [callback])
        return this.log(level, ...args);
      };
    });
  }
}

/**
 * Create a new instance of a winston Logger. Creates a new
 * prototype for each instance.
 * @param {!Object} opts - Options for the created logger.
 * @returns {Logger} - A newly created logger instance.
 */
module.exports = (opts = { levels: config.npm.levels }) => (
  new DerivedLogger(opts)
);
