/*jshint eqeqeq:false, eqnull:true, browser:true, evil:true, bitwise:false */
/*globals COMSCORE, addSWF, URI, TMI, App, escape, _gaq, mixpanel, spade, console, Base64 */

// = require base64
// = require mixpanel
// = require spade
// = require URI

//
// Twitch tracking interface
// Dependencies:
//   - twitch.core
//   - twitch.storage
//

// for Google Analytics tracking everywhere
window._gaq = window._gaq || [];

(function (Twitch, $) {
  var tracking = {};
  var isPrerender = /Prerender/.test(window.navigator.userAgent);

  /**
   * @enum
   */
  var Libraries = {
    MIXPANEL: '_mixpanel_',
    SPADE: '_spade_'
  };

  var trackEvent = function (event, properties, library) {
    if (isPrerender) {
      return;
    }

    properties = properties || {};

    if (!properties['distinct_id']) {
      properties['distinct_id'] = Twitch.idsForMixpanel.getOrCreateUniqueId();
    }

    properties['device_id'] = Twitch.idsForMixpanel.getOrCreateUniqueId();
    properties['tab_session_id'] = Twitch.idsForMixpanel.getOrCreateSessionStorageUniqueId();

    Twitch.geo.then(function (info) {
      // we want the properties object to not be overriden if these are defined
      properties = _.extend({
        host: window.document.location.host || "",
        domain: window.document.domain || "",
        'logged_in': Twitch.user.isLoggedIn(),
        login: Twitch.user.login(),
        url: window.document.location.href || "",
        'referrer_host': new RegExp("^https?:\/\/([^\/]+)").exec(window.document.referrer) || "",
        'referrer_url': window.document.referrer || "",
        'preferred_language': info['preferred_language'],
        'received_language': info['received_language']
      }, properties);

      if (library === Libraries.SPADE) {
        spade.track(event, properties);
      } else if (library === Libraries.MIXPANEL) {
        Twitch.tracking.mixpanel.sendMixpanelBeacon(event, properties);
      }
      Twitch.tracking.log.trackEvent(event, properties, library);
    });
  };

  /** pageview tracking diagnostics */

  var Diagnostics = function () {
    this.pageViewId = null;
    this.startTime = null;
  };

  Diagnostics.prototype.start = function () {
    // correctness contingent on subject page view
    // leaving device / distinct id unset on its properties hash
    var deviceId = Twitch.idsForMixpanel.getOrCreateUniqueId(),
        url = window.location.href,
        now = new Date().getTime(),
        currentRoute = window.App.__container__.lookup("controller:application").get("currentRouteName");

    this.pageViewId = Diagnostics._getPageViewId(deviceId);

    var properties = {
      'device_id': deviceId,
      location: currentRoute,
      'pageview_id': this.pageViewId,
      time: now / 1000,
      url: url // this is part of default tracked params
    };
    this.startTime = now;

    Twitch.tracking.spade.trackEvent('pageview_diagnostic_opportunity', properties);
  };

  Diagnostics.prototype.end = function () {
    var now = new Date().getTime();

    Twitch.tracking.spade.trackEvent('pageview_diagnostic_complete', {
      'device_id': Twitch.idsForMixpanel.getOrCreateUniqueId(),
      'pageview_id': this.pageViewId,
      // elapsed calculated manually on client since sometimes, both members
      // of event pair do not make it to spade due to network issues or some other reasons TBD.
      elapsed: (now - this.startTime) / 1000,
      time: now / 1000
    });
  };

  Diagnostics._getPageViewId = function (deviceId) {
    return (new Date().getTime()) + "-" + Twitch.idsForMixpanel.createUniqueId(deviceId);
  };

  /** Twitch.tracking.funnel */

  var queryParams = new URI().query(true);
  if (window.history && typeof window.history.replaceState === 'function') {
    var cleanedURI = new URI().removeQuery(["tt_medium", "tt_content", "tt_content_index", "followed_flow"]);
    window.history.replaceState({}, null, cleanedURI.toString());
  }
  var trackPageViewProperties = {};

  tracking.funnel = {

    /**
     * all page views events that require additional API should be wrapped by this
     *
     * @param {Promise | RSVP.Promise} pageViewPropertiesPromise
     * @return {Promise}
     */
    trackPageViewWithDiagnostics: function (pageViewPropertiesPromise) {
      var diag = new Diagnostics();
      diag.start();
      return pageViewPropertiesPromise.then(function (pageViewProperties) {
        Twitch.tracking.funnel.trackPageView(pageViewProperties);
        diag.end();
      });
    },

    data: {
      geo: Twitch.geo,
      user: new RSVP.Promise(function (resolve) {
        if (Twitch.user.isLoggedIn()) {
          Twitch.user(resolve, function () { resolve({}); });
        } else {
          resolve({});
        }
      }),
      sentinel: Twitch.sentinel.detect.catch(function (error) {
        return undefined;
      })
    },

    addCampaignTracking: function (options) {
      $(options.selector).each(function (i, element) {
        $(element).attr({
          'data-tt_medium':  options['tt_medium'],
          'data-tt_content': options['tt_content']
        });
      });
    },

    campaignSelector: '*[data-tt_medium], *[data-tt_content]',

    /** This function is used to add campaign tracking to any link which has data-tt_medium or data-tt_content.
        It works for plain old hyperlinks, and it works for Ember transitions. */
    handleCampaignClick: function (event) {
      var element  = $(event.target).closest(tracking.funnel.campaignSelector);
      if (element.length) {
        var contentIndex = parseInt(element.attr('data-tt_content_index')) || undefined;
        var properties = {
          medium: element.attr('data-tt_medium'),
          content: element.attr('data-tt_content'),
          content_index: contentIndex
        };

        if (element[0].hasAttribute('data-ember-action')) {
          Twitch.tracking.funnel.addProperties(properties);
        } else if (element.prop("tagName").toLowerCase() === 'a') {
          var href = element.attr('href') || '';
          element.attr('href', URI(href).addSearch({
            "tt_medium":  element.attr('data-tt_medium'),
            "tt_content": element.attr('data-tt_content'),
            "tt_content_index": contentIndex
          }).toString());

          Twitch.tracking.funnel.addProperties(properties);
        }
      }
    },

    // Used for adding properties before the didTransition event is hit
    addProperties: function (properties) {
      trackPageViewProperties = _.extend(trackPageViewProperties, properties);
    },

    pageviewProperties: {},

    trackPageView: function (properties) {
      properties = properties || {};

      var uri         = new URI(),
          referrer    = properties.referrer || document.referrer,
          referrerUri = referrer ? new URI(referrer) : {},
          mwcc = URI.parseQuery(window.location.search).mwcc;

      if (mwcc) {
        properties.exp_name = 'mwcc';
        properties.exp_grp = mwcc;
      }

      RSVP.hash(this.data).then(function (data) {
        properties = _.defaults(trackPageViewProperties, properties || {}, {
          // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
          session_device_id: Twitch.idsForMixpanel.getOrCreateSessionUniqueId(), // TODO: id durability investigation for science, remove when done.
          localstorage_device_id: Twitch.idsForMixpanel.getOrCreateLocalStorageUniqueId(),
          app_version: undefined,
          is_turbo: !!data.user.has_turbo,
          language: data.geo.received_language,
          viewport_height: $(window).height(),
          viewport_width: $(window).width(),
          collapse_left: undefined,
          collapse_right: undefined,
          referrer_host: referrer ? referrerUri.host() : undefined,
          referrer_domain: referrer ? referrerUri.domain() : undefined,
          accept_language: undefined,
          medium: queryParams.tt_medium,
          content: queryParams.tt_content,
          content_index: queryParams.tt_content_index ? parseInt(queryParams.tt_content_index) : undefined,
          time: new Date().getTime() / 1000,
          location: undefined,
          platform: 'web',
          login: data.user.login,
          url: uri.toString(),
          host: uri.host(),
          domain: uri.domain(),
          adblock: undefined,
          sentinel_ab: data.sentinel,
          browser: navigator.userAgent,
          page_session_id: Twitch.idsForMixpanel.createUniqueId(),
          referrer: referrer || undefined
          // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
        });
        queryParams = {};
        trackPageViewProperties = {};

        // expose some pageview params to the client
        Twitch.tracking.funnel.pageviewProperties = {
          location: properties.location,
          content: properties.content,
          medium: properties.medium,
          page_session_id: properties.page_session_id
        };

        if (Twitch.tracking.funnel.debugEnabled()) {
          console.group('Twitch.tracking.funnel.trackPageView');
          console.log('event:', 'pageview');
          console.log('properties:', properties);
          console.groupEnd();
        }
        tracking.spadeAndMixpanel.trackEvent('pageview', properties);
      });

      /** TODO: remove this garbage */
      sendArtificialTrafficToAwsDelayed();

    },

    debugEnabled: function (enabled) {
      var DEBUG_ENABLED_FLAG = 'Twitch.tracking.funnel.debugEnabled';
      if (enabled === undefined) {
        return Twitch.storage.getObject(DEBUG_ENABLED_FLAG) === true;
      } else {
        Twitch.storage.setObject(DEBUG_ENABLED_FLAG, enabled);
      }
    }

  };



  /** This function is a hacky way to send artificial traffic to AWS for load testing.
      Depends on arbkey aws_artificial_load_factor.
        <= 1   probability the client will make 1 request
         > 1   client will always make this many requests */
  var sendArtificialTrafficToAwsDelayed = function () {
    var WAIT_TO_SEND_MILLISECONDS = 2000,
        AWS_SECURE_LOGIN_URL = 'https://secure-login.twitch.tv/lc',
        iterations = getAwsArtificialTrafficIterations();
    setTimeout(function () {
      try {
        for (var i = 0; i < iterations; i++) {
          $('<img style="display:none;" src="' + AWS_SECURE_LOGIN_URL + '?q=' + Math.floor(Math.random() * 1000000000) + '" />').prependTo($('body'));
        }
      } catch (e) { }
    }, WAIT_TO_SEND_MILLISECONDS);
  };

  var getAwsArtificialTrafficFactor = _.memoize(function () {
    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
    return parseFloat(SiteOptions.aws_artificial_load_factor || 0, 10);
    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
  });

  var getAwsArtificialTrafficIterations = function () {
    var loadFactor = getAwsArtificialTrafficFactor();
    if (loadFactor <= 1) return Math.random() < loadFactor ? 1 : 0;
    else return Math.ceil(loadFactor);
  };



  tracking.log = {

    getTrackingLog: function () {
      return Twitch.storage.getObject("trackingLog");
    },

    trackEvent: function (evt, properties, library) {
      var trackingLog = Twitch.storage.getObject("trackingLog") || [];
      var trackingLogLength = this.getTrackingLogLength();
      if (trackingLog.length > trackingLogLength) {
        trackingLog.shift();
      }
      var time = new Date();
      trackingLog.push({event: evt, properties: properties, createdOn: time, library: library});
      this.consoleLogging(evt, properties, library);
      Twitch.storage.setObject("trackingLog", trackingLog);
    },

    consoleLogging: function (evt, properties, library) {
      if (Twitch.tracking.log.debugEnabled()) {
        console.group('Twitch.tracking.log.trackEvent');
        console.log('library:', library);
        console.log('event:', evt);
        console.log('properties:', properties);
        console.groupEnd();
      }
    },

    setTrackingLogLength: function (trackingLogLength) {
      Twitch.storage.setObject("trackingLogLength", trackingLogLength);
    },

    getTrackingLogLength: function () {
      return Twitch.storage.getObject("trackingLogLength") || 5;
    },

    debugEnabled: function (enabled) {
      var TRACKING_LOG_ENABLED_FLAG = 'Twitch.tracking.log.debugEnabled';
      if (enabled === undefined) {
        return Twitch.storage.getObject(TRACKING_LOG_ENABLED_FLAG) === true;
      } else {
        Twitch.storage.setObject(TRACKING_LOG_ENABLED_FLAG, enabled);
      }
    }

  };

  $(document).on('click', tracking.funnel.campaignSelector, tracking.funnel.handleCampaignClick);

  tracking.gaq = {
    trackPageView: function (path) {
      _gaq.push(path ? ['_trackPageview', path] : ['_trackPageview']);
    },

    trackVirtualPageView: function (page) {
      _gaq.push(["_trackPageview", decodeURIComponent(page)]);
    },

    trackChannelPageView: function (channel, subPath, game) {
      Twitch.tracking.gaq.trackPageView('/channel/' + escape(channel || '') + subPath + '?game=' + escape(game || ''));
    }

  };

  tracking.spadeAndMixpanel = {
    trackEvent: function (event, properties) {
      tracking.spade.trackEvent(event, properties);
      tracking.mixpanel.trackEvent(event, properties);
    }
  };

  tracking.spade = {
    trackEvent: function (event, properties) {
      trackEvent(event, properties, Libraries.SPADE);
    }
  };

  tracking.mixpanel = {
    trackDiscovery: function () {
      // Track an autocomplete search event if the user has just performed an autocompleted search query
      var autocompleteType = Twitch.storage.get("mp:autocomplete-referrer"); // (live, users, or broadcasts)
      var referrerHostName = URI.parse(document.referrer).hostname;
      if (autocompleteType) {
        Twitch.tracking.mixpanel.trackEvent("discovery", {
          "action": "autocomplete",
          "autocomplete_type": autocompleteType
        });
        Twitch.storage.del("mp:autocomplete-referrer");
      } else if (["front_page", "directory", "search", "team"].indexOf(window.SitePageType) !== -1) {
        Twitch.tracking.mixpanel.trackEvent("discovery", {
          "action": window.SitePageType,
          "game": (window.PP && window.PP.channelMetaGame) || null
        });
      } else if (window.SitePageType === "channel" && window.PP &&
          (Twitch.storage.get("mp:source") || (referrerHostName && !referrerHostName.match("twitch.tv")))) {
        // We are currently tracking channel pages only if "mp:source"
        // is set or the user has come from an external site
        Twitch.api.get('streams/' + window.PP.channel)
        .done(function (data) {
          var properties = {
            "action": "channel",
            "source": Twitch.storage.get("mp:source") || referrerHostName,
            "is_live": data.stream != null
          };
          if (Twitch.storage.get("mp:channel-discovery-index")) {
            properties.index = Twitch.storage.get("mp:channel-discovery-index");
          }
          Twitch.tracking.mixpanel.trackEvent("discovery", properties);
          Twitch.storage.del("mp:source");
          Twitch.storage.del("mp:channel-discovery-index");
        });
      }
    },

    trackGameClick: function () {
      var gameClick = Twitch.storage.get("mp:game-click-name");
      if (gameClick) {
        var properties = {
          'game': gameClick,
          'promoted': Twitch.storage.get("mp:game-promoted"),
          'location': Twitch.storage.get("mp:game-click-location"),
          'index': Twitch.storage.set("mp:game-click-index")
        };
        Twitch.tracking.mixpanel.trackEvent("game-directory-click", properties);
        Twitch.storage.del("mp:game-click-name");
        Twitch.storage.del("mp:game-promoted");
        Twitch.storage.del("mp:game-click-location");
        Twitch.storage.del("mp:game-click-index");
      }
    },

    setGameClickVars: function (properties) {
      Twitch.storage.set("mp:game-click-name", properties.game);
      Twitch.storage.set("mp:game-click-index", properties.index);
      Twitch.storage.set("mp:game-click-location", properties.location);
      Twitch.storage.set("mp:game-promoted", properties.promoted);
    },

    trackEvent: function (evt, properties) {
      var bornProperties;
      switch (evt) {
        case "chat":
          if (Twitch.storage.get('chat_sent') != 'true') {
            Twitch.storage.set('chat_sent', true);
            bornProperties = _.extend({context: evt}, properties);
            trackEvent("$born", bornProperties, Libraries.MIXPANEL);
          }
          break;
        case "video-play":
          if (Twitch.storage.get('video_played') != 'true') {
            Twitch.storage.set('video_played', true);
            bornProperties = _.extend({context: evt}, properties);
            trackEvent("$born", bornProperties, Libraries.MIXPANEL);
          }
          break;
      }

      trackEvent(evt, properties, Libraries.MIXPANEL);
    },

    sendMixpanelBeacon: function (evt, properties) {
      properties = _.extend(properties, {token: mixpanel.config.token, time: Date.now(), ip: 1});

      var data = {
        event: evt,
        properties: properties
      };

      var url = "//api.mixpanel.com/track?data=" + Base64.encode(JSON.stringify(data));

      $.ajax({url: url, crossDomain: true});
    }
  };

  tracking.getComscoreScript = _.memoize(function () {
    var comscoreUrl = ((document.location.protocol == 'https:') ? 'https://sb' : 'http://b') +
      '.scorecardresearch.com/beacon.js';
    return $.ajax({
      url: comscoreUrl,
      crossDomain: true,
      dataType: 'script'
    });
  });

  tracking.sendComscoreBeacon = function (opts) {
    tracking.getComscoreScript().then(function () {
      if (typeof COMSCORE !== 'undefined') {
        var getComscore15 = function (sourceCookie) {
          return (sourceCookie === null || sourceCookie === "CXCXCXCX") ? "" : sourceCookie;
        };
        COMSCORE.beacon({
          c1: opts.c1,
          c2: opts.c2,
          c3: opts.c3,
          c4: "",
          c5: opts.c5,
          c6: "",
          c15: getComscore15(Twitch.storage.legacy.get("psHashedUserId"))
        });
      }
    });
  };

  tracking.sendGermanBeacons = function () {
    Twitch.geo.then(function (response) {
      function getGermanPageCode() {
        switch (window.SitePageType) {
        case 'directory':
          // Return 1148gameVIDE if on game-specific directory, 1148directory otherwise
          // We cannot determine this from Backbone so we are using URI
          return URI().segment(1) === 'game' ? '1148gameVIDE' : '1148directory';
        case 'search':
          return '4201search';
        case 'front_page':
          return '1101homepage';
        case 'channel':
          // fall-through
        case 'video':
          return '1148publVIDE';
        case 'video_list':
          return '1148publcont';
        default:
          return null;
        }
      }
      // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
      if (response.geo !== 'DE' && response.received_language !== 'de') {
        // Neither user IP location nor user language settings are German
        return;
      }
      // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

      var germanPageCode = getGermanPageCode();

      if (!germanPageCode) {
        // Not a page we care about
        return;
      }

      // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
      if (response.geo === 'DE' && response.received_language !== 'de') {
        // IP location is German, but user language settings are not.
        germanPageCode += '_multilanguage';
      }
      // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

      // SZM Beacon
      $.getScript("https://script.ioam.de/iam.js", function () {
        var iamData = {
          "st": "twitchtv", /* site name */
          "cp": germanPageCode,
          "oc": germanPageCode,
          "mg": "yes", /* enable SZM's migration mode, meaning we can delete the old tracking code */
          "sv": window.SitePageType === 'front_page' ? "ke" : "in" /* enable survey delivery */
        };
        window.iom.c(iamData, 1);
      });
    });
  };

  var channelNameThatYouJustLoginAndFollowed = '';
  tracking.didJustLoginAndFollow = function (channelName) {
    if (channelNameThatYouJustLoginAndFollowed === channelName) {
      channelNameThatYouJustLoginAndFollowed = '';
      return true;
    } else {
      return false;
    }
  };

  Twitch.mixin({
    tracking: tracking
  });

  var trackApiError = function (error, properties) {
    var parsedURL = URI.parse(error.url);
    var currentPath;

    // TODO: This should get moved to web-client. We shouldn't be reaching into Ember from web/web.
    if (window.App && window.App.get('currentPath') !== undefined) {
      currentPath = window.App.get('currentPath');
    } else if (window.App && window.App.__container__) {
      currentPath = window.App.__container__.lookup('service:globals').get('currentPath');
    }

    tracking.spade.trackEvent('api-error', $.extend({
      platform: 'web',
      application: currentPath,
      'service_host': parsedURL.hostname,
      endpoint: parsedURL.path,
      'error_code': error.jqXHR.status
    }, properties));
  };

  Twitch.api.on('fail', trackApiError);
  /** This hack is to pass context information about login and follow from before the page refresh. */
  // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
  if (queryParams.followed_flow) {
    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
    var loginAndFollowTrackingData = _.defaults(
      Twitch.cache.read('LoginAndFollowTrackingData') || {},
      {platform: 'web'},
      _.pick(queryParams, 'followed_flow')
    );
    /** This setTimeout is required to make sure tracking.spadeAndMixpanel.trackEvent happens after the entire Twitch library has loaded */
    channelNameThatYouJustLoginAndFollowed = loginAndFollowTrackingData.channel;
    setTimeout(function () {
      tracking.spadeAndMixpanel.trackEvent('follow', loginAndFollowTrackingData);
    });
    Twitch.cache.write('LoginAndFollowTrackingData', null);
  }

  if (window.TMI) {
    $(function () {
      TMI.on('api-fail', function (error) {
        trackApiError(error, {
          application: 'tmi-client'
        });
      });
    });
  }

  _gaq.push(
    ['_setAccount', 'UA-23719667-1'],
    ['_setDomainName', "twitch.tv"],
    ['_setSampleRate', '1'],
    ['_setAllowLinker', true],
    // http://code.google.com/apis/analytics/docs/tracking/gaTrackingCustomVariables.html
    ['_setCustomVar', 1, "UserType", Twitch.user.isLoggedIn() ? "user" : "visitor", 2],
    // enhanced link attribution (see https://support.google.com/analytics/answer/2558867)
    ['_require', 'inpage_linkid', '//www.google-analytics.com/plugins/ga/inpage_linkid.js']
  );

})(Twitch, jQuery);
