/* globals Twitch */
import Service from 'ember-service';
import { assert } from 'ember-metal/utils';
import { isPresent } from 'ember-utils';
import computed from 'ember-computed';
import requestIdleCallback from 'web-client/utilities/request-idle-callback';

const { benchmark } = Twitch;
const { funnel, mixpanel, spade } = Twitch.tracking;

export default Service.extend({
  benchmark,
  funnel,

  init() {
    this._super(...arguments);
    this._trackedEvents = {};
    this._flushScheduled = false;

    /*
     * Some events run before this service starts. Read them from
     * window.lazyEvents and flush if needed.
     */
    if (window.lazyEvents && window.lazyEvents.length !== 0) {
      this._events = window.lazyEvents;
      this._lazyFlush();
    } else {
      this._events = [];
    }

    /*
     * Install a flush method for benchmark library, which is detached from
     * the container setup.
     */
    window.lazyEventsFlush = () => this._lazyFlush();
  },

  willDestroy() {
    window.lazyEventsFlush = null;
    this._super(...arguments);
  },

  _flush() {
    let services = this.get('services');
    for (let i=0;i<this._events.length;i++) {
      let { services: serviceNames, event, data = {} } = this._events[i];

      data.platform = data.platform || 'web';

      services.forEach(service => {
        if (!serviceNames || serviceNames.includes(service.name)) {
          service.trackEvent(event, data);
        }
      });
    }
    this._events.length = 0;
  },

  _lazyFlush() {
    if (!this._flushScheduled) {
      requestIdleCallback(() => {
        if (this.isDestroyed) { return; }
        this._flushScheduled = false;
        this._flush();
      });
      this._flushScheduled = true;
    }
  },

  /**
   * Send an event to track any link which has data-tt_medium or data-tt_content.
   *
   * @method handleCampaignClick
   * @param {Object} event An event object from the DOM
     */
  handleCampaignClick(event) {
    this.get('funnel').handleCampaignClick(event);
  },

  /**
   * Send an event to Twitch analytics services
   *
   * @method trackEvent
   * @param {Object} config Single configuration parameter that expects the following attributes:
   * @param {Array} config.services [Optional] Strings indicating the services to send this event to (e.g. 'mixpanel')
   * @param {String} config.event Name of the event
   * @param {Object} config.data Properties to pass to each service
     */
  trackEvent(config) {
    assert('Tracking Service: An event name must be passed to trackEvent().', isPresent(config));

    this._events.push(config);
    this._lazyFlush();
  },

  /**
   * Send a channel event to Twitch analytics services
   *
   * @method trackChannelEvent
   * @param {String} event Name of the event
   * @param {Object} channel Channel model
   * @param {Object} data Properties to pass to each service
     */
  trackChannelEvent(event, channel, data = {}) {
    data.follow_count = channel.get('followersTotal');

    this.trackEvent({ event, data });
  },

  /**
   * Used to track interactions with the bits chat card.
   *
   * @method trackBitsCardInteractions
   * @param {String} actionName the name of the interaction with the card i.e. 'menu_open', 'buy_main','buy_100'
   * @param {String} userName The username of the logged in user
   * @param {Object} bitsBalance number of bits currently displayed to the user
   */
  trackBitsCardInteractions(actionName, userName, bitsBalance, emoteType = null, emoteTier = null, asins = null) {
    let eventData = {
        client_time: new Date().getTime(),
        device_id: Twitch.idsForMixpanel.getOrCreateUniqueId(),
        login: userName,
        action_name: actionName,
        action_type: 'click',
        displayed_balance: bitsBalance,
        adblock: Twitch.sentinel.detect._result,
        emote_type: emoteType,
        emote_tier: emoteTier,
        asin_list: asins
    };

    this.trackEvent({ event: 'bits_card_interaction', data: eventData });
  },

  /**
   * Send an event to Twitch analytics service, but only once per page session
   *
   * @param {String} config.event Name of the event
   * @param {String} config.namespace Value to represent this as a single event
   */
  trackEventOnce({ event, namespace }) {
    namespace = namespace || event;
    if (!this._trackedEvents[namespace]) {
      this.trackEvent(...arguments);
      this._trackedEvents[namespace] = true;
    }
  },

  /**
   * The action below provide a public API for templates to access the service's methods
   *
   * Ex: {{my-component track=(action "trackEvent" target=tracking)}}
   */
  actions: {
    trackEvent() {
      this.trackEvent(...arguments);
    },

    trackChannelEvent() {
      this.trackChannelEvent(...arguments);
    }
  },

  /**
   * Array of analytics services supported by the tracking service
   *
   * Each service object should implement the following properties:
   * {String} name Name of the analytics service
   * {Function} trackEvent Method passing event name & data to the specified analytics service
   *
   * @property services
   * @type {Array}
   */
  services: computed(function() {
    return [{
      name: 'mixpanel',

      trackEvent(eventName, data = {}) {
        mixpanel.trackEvent(eventName, data);
      }
    }, {
      name: 'spade',

      trackEvent(eventName, data = {}) {
        spade.trackEvent(eventName, data);
      }
    }];
  })
});
