text: /* eslint no-console:0 */
  
  'use strict';
  
  const debug = require('debug')('log4js:loggly');
  const loggly = require('node-loggly-bulk');
  const os = require('os');
  
  function isAnyObject(value) {
    return value !== null && (typeof value === 'object' || typeof value === 'function');
  }
  
  function numKeys(obj) {
    return Object.keys(obj).length;
  }
  
  /**
   * @param msgListArgs
   * @returns Object{ deTaggedMsg: [...], additionalTags: [...] }
   */
  function processTags(msgListArgs) {
    const msgList = (msgListArgs.length === 1 ? [msgListArgs[0]] : msgListArgs);
  
    return msgList.reduce((accumulate, element) => {
      if (isAnyObject(element) && Array.isArray(element.tags) && numKeys(element) === 1) {
        accumulate.additionalTags = accumulate.additionalTags.concat(element.tags);
      } else {
        accumulate.deTaggedData.push(element);
      }
      return accumulate;
    }, { deTaggedData: [], additionalTags: [] });
  }
  
  /**
   * Loggly Appender. Sends logging events to Loggly using node-loggly, optionally adding tags.
   *
   * This appender will scan the msg from the logging event, and pull out any argument of the
   * shape `{ tags: [] }` so that it's possible to add tags in a normal logging call.
   *
   * For example:
   *
   * logger.info({ tags: ['my-tag-1', 'my-tag-2'] }, 'Some message', someObj, ...)
   *
   * And then this appender will remove the tags param and append it to the config.tags.
   *
   * @param config object with loggly configuration data
   * {
   *   token: 'your-really-long-input-token',
   *   subdomain: 'your-subdomain',
   *   tags: ['loggly-tag1', 'loggly-tag2', .., 'loggly-tagn']
   * }
   * @param layout a function that takes a logevent and returns a string (defaults to objectLayout).
   */
  function logglyAppender(config, layout) {
    const client = loggly.createClient(config);
    let openRequests = 0;
    let shutdownCB;
  
    debug('creating appender.');
  
    function app(loggingEvent) {
      const result = processTags(loggingEvent.data);
      const deTaggedData = result.deTaggedData;
      const additionalTags = result.additionalTags;
  
      // Replace the data property with the deTaggedData
      loggingEvent.data = deTaggedData;
  
      const msg = layout(loggingEvent);
  
      openRequests += 1;
      debug('sending log event to loggly');
      client.log(
        {
          msg: msg,
          level: loggingEvent.level.levelStr,
          category: loggingEvent.categoryName,
          hostname: os.hostname().toString(),
        },
        additionalTags,
        (error) => {
          if (error) {
            console.error('log4js.logglyAppender - error occurred: ', error);
          }
  
          debug('log event received by loggly.');
  
          openRequests -= 1;
  
          if (shutdownCB && openRequests === 0) {
            shutdownCB();
  
            shutdownCB = undefined;
          }
        }
      );
    }
  
    app.shutdown = function (cb) {
      debug('shutdown called');
      if (openRequests === 0) {
        cb();
      } else {
        shutdownCB = cb;
      }
    };
  
    return app;
  }
  
  function configure(config, layouts) {
    let layout = layouts.messagePassThroughLayout;
    if (config.layout) {
      layout = layouts.layout(config.layout.type, config.layout);
    }
    debug('configuring new appender');
    return logglyAppender(config, layout);
  }
  
  module.exports.configure = configure;
  