'use strict';
const path = require('path');
const isEmpty = require('lodash.isempty');
const GraphiteSender = require('./graphite-sender');
const StatsDSender = require('./statsd-sender');
const merge = require('lodash.merge');
const get = require('lodash.get');
const log = require('intel').getLogger('sitespeedio.plugin.graphite');
const sendAnnotations = require('./send-annotation');
const DataGenerator = require('./data-generator');
const graphiteUtil = require('../../support/tsdbUtil');
const isStatsd = require('./helpers/is-statsd');
const throwIfMissing = require('../../support/util').throwIfMissing;
const cliUtil = require('../../cli/util');

module.exports = {
  name() {
    return path.basename(__dirname);
  },

  /**
   * Define `yargs` options with their respective default values. When displayed by the CLI help message
   * all options are namespaced by its plugin name.
   *
   * @return {Object<string, require('yargs').Options} an object mapping the name of the option and its yargs configuration
   */
  get cliOptions() {
    return require(path.resolve(__dirname, 'cli.js'));
  },

  open(context, options) {
    throwIfMissing(options.graphite, ['host'], 'graphite');
    const opts = merge({}, this.config, options.graphite);
    this.options = options;
    this.perIteration = get(opts, 'perIteration', false);
    const SenderConstructor = isStatsd(opts) ? StatsDSender : GraphiteSender;

    this.filterRegistry = context.filterRegistry;
    this.sender = new SenderConstructor(opts.host, opts.port, opts.bulkSize);
    this.dataGenerator = new DataGenerator(
      opts.namespace,
      opts.includeQueryParams,
      options
    );
    log.debug(
      'Setting up Graphite %s:%s for namespace %s',
      opts.host,
      opts.port,
      opts.namespace
    );
    this.timestamp = context.timestamp;
    this.resultUrls = context.resultUrls;
    this.messageTypesToFireAnnotations = [];
    this.receivedTypesThatFireAnnotations = {};
    this.make = context.messageMaker('graphite').make;
    this.sendAnnotation = true;
    this.alias = {};
    this.wptExtras = {};
    this.usingBrowsertime = false;
  },

  processMessage(message, queue) {
    // First catch if we are running Browsertime and/or WebPageTest
    if (message.type === 'browsertime.setup') {
      this.messageTypesToFireAnnotations.push('browsertime.pageSummary');
      this.usingBrowsertime = true;
    } else if (message.type === 'webpagetest.setup') {
      this.messageTypesToFireAnnotations.push('webpagetest.pageSummary');
    } else if (message.type === 'browsertime.config') {
      if (message.data.screenshot) {
        this.useScreenshots = message.data.screenshot;
        this.screenshotType = message.data.screenshotType;
      }
    } else if (message.type === 'sitespeedio.setup') {
      // Let other plugins know that the Graphite plugin is alive
      queue.postMessage(this.make('graphite.setup'));
    } else if (message.type === 'grafana.setup') {
      this.sendAnnotation = false;
    } else if (message.type === 'browsertime.browser') {
      this.browser = message.data.browser;
    } else if (
      message.type === 'webpagetest.browser' &&
      !this.usingBrowsertime
    ) {
      // We are only interested in WebPageTest browser if we run it standalone
      this.browser = message.data.browser;
    }

    if (message.type === 'browsertime.alias') {
      this.alias[message.url] = message.data;
    }

    const filterRegistry = this.filterRegistry;
    if (
      !(
        message.type.endsWith('.summary') ||
        message.type.endsWith('.pageSummary') ||
        (this.perIteration && message.type.endsWith('.run'))
      )
    ) {
      return;
    }

    if (this.messageTypesToFireAnnotations.includes(message.type)) {
      this.receivedTypesThatFireAnnotations[message.url]
        ? this.receivedTypesThatFireAnnotations[message.url]++
        : (this.receivedTypesThatFireAnnotations[message.url] = 1);
    }

    if (message.type === 'webpagetest.pageSummary') {
      this.wptExtras[message.url] = {};
      this.wptExtras[message.url].webPageTestResultURL =
        message.data.data.summary;
      this.wptExtras[message.url].connectivity = message.connectivity;
      this.wptExtras[message.url].location = graphiteUtil.toSafeKey(
        message.location
      );
    }

    // we only sends individual groups to Graphite, not the
    // total of all groups (you can calculate that yourself)
    if (message.group === 'total') {
      return;
    }
    message = filterRegistry.filterMessage(message);
    if (isEmpty(message.data)) return;

    // TODO Here we could add logic to either create a new timestamp or
    // use the one that we have for that run. Now just use the one for the
    // run
    const dataPoints = this.dataGenerator.dataFromMessage(
      message,
      this.timestamp,
      this.alias
    );

    if (dataPoints.length > 0) {
      const data = dataPoints.join('\n') + '\n';
      return this.sender.send(data).then(() => {
        // Make sure we only send the annotation once per URL:
        // If we run browsertime, always send on browsertime.pageSummary
        // If we run WebPageTest standalone, send on webPageTestSummary
        // when we configured a base url

        if (
          this.receivedTypesThatFireAnnotations[message.url] ===
            this.messageTypesToFireAnnotations.length &&
          this.resultUrls.hasBaseUrl() &&
          this.sendAnnotation
        ) {
          this.receivedTypesThatFireAnnotations[message.url] = 0;
          const absolutePagePath = this.resultUrls.absoluteSummaryPagePath(
            message.url
          );
          return sendAnnotations.send(
            message.url,
            message.group,
            absolutePagePath,
            this.useScreenshots,
            this.screenshotType,
            this.timestamp,
            this.alias,
            this.wptExtras[message.url],
            this.usingBrowsertime,
            this.browser,
            this.options
          );
        }
      });
    } else {
      return Promise.reject(
        new Error(
          'No data to send to graphite for message:\n' +
            JSON.stringify(message, null, 2)
        )
      );
    }
  },

  get config() {
    return cliUtil.pluginDefaults(this.cliOptions);
  }
};
