/* globals _ */
import Service from 'ember-service';
import injectService from 'ember-service/inject';
import ContextBoundTasksMixin from '../mixins/context-bound-tasks';
import { Stats } from 'client-event-reporter';
import ENV from '../config/environment';

const LEVEL_3 = 'level3';
const AKAMAI = 'akamai';
const CLOUDFRONT = 'cloudfront';
const LIVE = 'live';
const ORIGIN = 'origin';

const HTML_ASSET = 'index-html';
const PNG_ASSET = '404-png';
const STATIC_CDN_JPG_ASSET = '404-preview-jpg';
const JS_ASSET = 'emberapp-js';
const CSS_ASSET = 'app-css';
const PASSPORT_ASSET = 'passport-en-js';
const PLAYER_ASSET = 'player-js';
const API_ASSET = 'sureroute-object';

const PNG_PATH = 'images/xarth/pages/error/error_castle.png';
const STATIC_CDN_JPG_PATH = 'ttv-static/404_preview-320x180.jpg';
const EMBER_PATH = 'emberapp.js';
const CSS_PATH = 'styles/application.css';
const TRANSLATION_PATH = 'javascripts/translations/en.js';
const PLAYER_PATH = 'js/player.js';
const API_TEST_PATH = 'sureroute.html';

const WWW = 'www.twitch.tv';
const WEB_CDN = 'web-cdn.ttvnw.net';
const STATIC_CDN = 'static-cdn.jtvnw.net';
const API_ORIGIN = 'api-origin.twitch.tv';

const WWW_AKAMAI = 'www-akamai.twitch.tv';
const WEB_CDN_AKAMAI = 'web-cdn-akamai.ttvnw.net';
const STATIC_CDN_AKAMAI = 'static-cdn-akamai.jtvnw.net';
const API_AKAMAI = 'api-akamai.twitch.tv';

const WWW_L3 = 'www-twitch-tv-l3.twitch.tv';
const WEB_CDN_L3 = 'web-cdn-l3.ttvnw.net';
const STATIC_CDN_L3 = 'static-cdn-l3.jtvnw.net';

const PASSPORT_CDN_CLOUDFRONT = 'passport-cdn.ttvnw.net';
const PLAYER_CDN_CLOUDFRONT = 'player.twitch.tv';

export default Service.extend(ContextBoundTasksMixin, {
  POLL_INTERVAL: 2.5*60*1000, // 2.5 minutes
  SAMPLE_SIZE: 2, // number of assets tested per POLL_INTERVAL

  ajax: injectService(),
  experiments: injectService(),
  stats: Stats.getInstance(ENV.environment, 'web-client.mixins.tracking-router.cdnExperiment.geoip_country'),
  tracking: injectService(),

  assets: [
    {group: LIVE, 'name': HTML_ASSET, 'url': `https://${WWW}`},
    {group: LIVE, 'name': PNG_ASSET, 'url': `https://${WWW}/${PNG_PATH}`},
    {group: LIVE, 'name': STATIC_CDN_JPG_ASSET, 'url': `https://${STATIC_CDN}/${STATIC_CDN_JPG_PATH}`},
    {group: LIVE, 'name': JS_ASSET, 'url': `https://${WEB_CDN}/${EMBER_PATH}`},
    {group: LIVE, 'name': CSS_ASSET, 'url': `https://${WEB_CDN}/${CSS_PATH}`},
    {group: LEVEL_3, 'name': HTML_ASSET, 'url': `https://${WWW_L3}`},
    {group: LEVEL_3, 'name': PNG_ASSET, 'url': `https://${WWW_L3}/${PNG_PATH}`},
    {group: LEVEL_3, 'name': STATIC_CDN_JPG_ASSET, 'url': `https://${STATIC_CDN_L3}/${STATIC_CDN_JPG_PATH}`},
    {group: LEVEL_3, 'name': JS_ASSET, 'url': `https://${WEB_CDN_L3}/${EMBER_PATH}`},
    {group: LEVEL_3, 'name': CSS_ASSET, 'url': `https://${WEB_CDN_L3}/${CSS_PATH}`},
    {group: AKAMAI, 'name': HTML_ASSET, 'url': `https://${WWW_AKAMAI}`},
    {group: AKAMAI, 'name': PNG_ASSET, 'url': `https://${WWW_AKAMAI}/${PNG_PATH}`},
    {group: AKAMAI, 'name': STATIC_CDN_JPG_ASSET, 'url': `https://${STATIC_CDN_AKAMAI}/${STATIC_CDN_JPG_PATH}`},
    {group: AKAMAI, 'name': JS_ASSET, 'url': `https://${WEB_CDN_AKAMAI}/${EMBER_PATH}`},
    {group: AKAMAI, 'name': CSS_ASSET, 'url': `https://${WEB_CDN_AKAMAI}/${CSS_PATH}`},
    {group: AKAMAI, 'name': API_ASSET, 'url': `https://${API_AKAMAI}/${API_TEST_PATH}`},
    {group: CLOUDFRONT, 'name': PASSPORT_ASSET, 'url': `https://${PASSPORT_CDN_CLOUDFRONT}/${TRANSLATION_PATH}`},
    {group: CLOUDFRONT, 'name': PLAYER_ASSET, 'url': `https://${PLAYER_CDN_CLOUDFRONT}/${PLAYER_PATH}`},
    {group: ORIGIN, 'name': API_ASSET, 'url': `https://${API_ORIGIN}/${API_TEST_PATH}`}
  ],

  monitor() {
    this.runTask(() => {
      let experiments = this.get('experiments');
      if (experiments) {
        experiments.getExperimentValue('NEXUS_ROLLOUT').then((cohort) => {
          if (cohort === 'enabled') {
            // If we shuffle the asset list once, we can just loop through it SAMPLE_SIZE elements at a time.
            this.set('assets', _.shuffle(this.get('assets')));
            this._runCDNExperiment();
          }
        });
      }
    }, this.get('POLL_INTERVAL'));
  },

  _runCDNExperiment() {
    if (this.isDestroyed) { return; }

    let assetsToDownload = this._chooseAssets();

    assetsToDownload.forEach((asset) => {
      this._fetchAsset(asset);
    });
    this.runTask(() => { this._runCDNExperiment(); }, this.get('POLL_INTERVAL'));
  },

  _chooseAssets() {
    let assets = this.get('assets');
    let sample = assets.splice(0, this.get('SAMPLE_SIZE'));
    assets.splice(assets.length, 0, ...sample);

    return sample;
  },

  _cacheKey() {
    let numAssets = this.get('assets').length;
    let msPerCycle = this.get('POLL_INTERVAL') * Math.floor(numAssets / this.get('SAMPLE_SIZE'));
    let timestamp = (new Date()).getTime();

    return Math.floor(timestamp / msPerCycle);
  },

  _fetchAsset({group, name, url}) {
    let startTime = Date.now();
    let settings = {
      timeout: 10000,
      dataType: 'text',
      data: {
        _cache: this._cacheKey()
      }
    };
    this.get('ajax').raw(url, settings).then(({ jqXHR }) => {
      let endTime = Date.now();
      this._trackCDNExperiment(true, group, name, jqXHR.statusCode().status, endTime - startTime);
    }, ({ jqXHR }) => {
      if (jqXHR && jqXHR.statusCode) {
        let endTime = Date.now();
        this._trackCDNExperiment(false, group, name, jqXHR.statusCode().status, endTime - startTime);
      } else {
        // Unexpected, non-ajax error. Throw upstream.
        throw jqXHR;
      }
    });
  },

  _trackCDNExperiment(success, group, assetName, statusCode, elapsedTime) {
    // Graphite
    let stats = this.stats;

    if (success) {
      stats.logCounter(`${group}.${assetName}.success`, 1);
      stats.logTimer(`${group}.${assetName}.latency`, elapsedTime);
    } else {
      let statusGroup = '4xx';
      if (statusCode >= 500) {
        statusGroup = '5xx';
      }
      stats.logCounter(`${group}.${assetName}.failure.${statusGroup}`, 1);
    }

    // Spade and Mixpanel
    let properties = {
      group: group,
      assetName: assetName,
      success: success,
      statusCode: statusCode,
      latency: elapsedTime
    };
    this.get('tracking').trackEvent({ event: 'web_cdn_latency', data: properties });
  }

});
