import QUnit from 'qunit';
import Ember from 'ember';

const { getOwner } = Ember;

// Check if obj1 includes obj2.
function objectIncludes(obj1, obj2) {
  return Object.keys(obj2).every(k => {
    return obj1[k] === obj2[k];
  });
}

function _getOwner() {
  let context = QUnit.config.current.testEnvironment;

  // getOwner(context) is set by ember-qunit for integration and unit tests
  // context.owner is set by our tests/helpers/module-for-acceptance.js for acceptance tests
  return getOwner(context) || context.owner;
}

/*
 Track a Mixpanel or Spade event that was triggered by the tracking service.

 Examples:

 ```js
 assert.trackEvent('frontpage-carousel-click', {
   'carousel_index': index,
   'promotion_was_scheduled': true,
   channel: 'day9tv',
   game: 'Starcraft'
 });

 @method assert.trackEvent
 @param {String} event The name of the event. e.g. "frontpage-carousel-click" (Required)
 @param {Object|Function} dataOrFn [Optional] If passed an object, `deepEqual` against the event data. If passed a function, run the function against the event data.
 @param {String} message [Optional] A human-readable description of what you're testing.
*/

QUnit.assert.trackEvent = function(event, dataOrFn, message) {
  let invocations = _getOwner().lookup('service:tracking')._invocations;

  if (!dataOrFn || typeof dataOrFn === 'string') {
    let events = invocations.map(invocation => invocation.event);
    message = dataOrFn || `events ${QUnit.dump.parse(events)} contains "${event}" event`;
    this.ok(events.indexOf(event) !== -1, message);

  } else if (typeof dataOrFn === 'function') {
    invocations.forEach(invocation => {
      if (invocation.event === event) {
        dataOrFn(invocation);
      }
    });

  } else if (typeof dataOrFn === 'object') {
    let result = invocations.some(invocation => {
      return invocation.event === event &&
        objectIncludes(invocation.data, dataOrFn);
    });

    if (!message) {
      if (result) {
        message = `received event ${event}`;
      } else {
        message = `failed, expected ${event} event with data ${QUnit.dump.parse(dataOrFn)} but received invocations: ${QUnit.dump.parse(invocations)}`;
      }
    }

    this.pushResult({
      result,
      actual: result,
      expected: true,
      message
    });
  }
};

/*
  Assert against the number of tracked events. Useful for ensuring that events are not
  tracked when they should not be.

  Example:

  ```js
  assert.trackEventCount('frontpage-carousel-click', 0);
  ```

 @method assert.trackEventCount
 @param {String} event The name of the event. e.g. "frontpage-carousel-click" (Required)
 @param {number} count The number of invocations expected for the given event name
 @param {String} message [Optional] A human-readable description of what you're testing
*/
QUnit.assert.trackEventCount = function(event, count, message) {
  let invocationCount = _getOwner().lookup('service:tracking')
    ._invocations
    .filter(invocation => invocation.event === event)
    .length;

  this.pushResult({
    result: invocationCount === count,
    actual: invocationCount,
    expected: count,
    message: message || `Expected ${count} ${event} events to be tracked and there were ${invocationCount}.`
  });
};

QUnit.assert.trackBenchmarkEvent = function (event, dataOrFn, message) {
  let invocations = _getOwner().lookup('service:tracking').benchmark._invocations;

  if (!dataOrFn || typeof dataOrFn === 'string') {
    let events = invocations.map((invocation) => invocation.name);
    message = dataOrFn || `events ${QUnit.dump.parse(events)} contains "${event}" event`;
    this.ok(events.includes(event), message);

  } else if (typeof dataOrFn === 'function') {
    invocations.forEach((invocation) => {
      if (invocation.name === event) {
        dataOrFn(invocation);
      }
    });

  } else if (typeof dataOrFn === 'object') {
    let result = invocations.some((invocation) => {
      return invocation.name === event &&
        objectIncludes(invocation.data, dataOrFn);
    });

    if (!message) {
      if (result) {
        message = `received event ${event}`;
      } else {
        message = `failed, expected ${event} event with data ${QUnit.dump.parse(dataOrFn)} but received invocations: ${QUnit.dump.parse(invocations)}`;
      }
    }

    this.pushResult({
      result,
      actual: result,
      expected: true,
      message
    });
  }
};
