import { moduleFor } from 'ember-qunit';
import test from 'ember-sinon-qunit/test-support/test';
import wait from 'ember-test-helpers/wait';
import EmberObject from 'ember-object';
import Service from 'ember-service';
import RSVP from 'rsvp';
import run from 'ember-runloop';
import TransitionBenchmarkTrackingMixin from 'web-client/mixins/transition-benchmark-tracking';

const ROUTE_NAME = 'foo.bar';
const CRITICAL_COMPONENTS = ['foo', 'bar'];
let resolve;

const interactivityStub = Service.extend({
  monitor() {
    return new RSVP.Promise((res) => {
      resolve = res;
    });
  },

  reset() {}
});

const trackingStub = Service.extend({ benchmark: { transition() {} } });
const documentVisibilityStub = Service.extend({ visible: true, lostVisibility: false});

moduleFor('mixin:transition-benchmark-tracking', 'Unit | Mixin | transition-benchmark-tracking', {
  beforeEach() {
    this.BaseObject = EmberObject.extend(TransitionBenchmarkTrackingMixin, {
      routeName: ROUTE_NAME,
      criticalComponents: CRITICAL_COMPONENTS
    });
    this.registry.register('test:subject', this.BaseObject);
    this.register('service:interactivity', interactivityStub);
    this.register('service:tracking', trackingStub);
    this.register('service:document-visibility', documentVisibilityStub);
  },

  subject(properties = {}) {
    let subject = this.container.lookup('test:subject');
    subject.setProperties(properties);
    return subject;
  }
});

test('isLeafRoute - truthy', function (assert) {
  let transition = { targetName: ROUTE_NAME };

  let isLeafRoute = this.subject().isLeafRoute(transition);

  assert.ok(isLeafRoute, 'correctly identifies leaf route');
});

test('isLeafRoute - falsey', function (assert) {
  let transition = { targetName: 'stuff.things' };

  let isLeafRoute = this.subject().isLeafRoute(transition);

  assert.notOk(isLeafRoute, 'correctly identifies non-leaf route');
});

test('isLeafRoute - truthy w/ saved transition', function (assert) {
  let transition = { targetName: ROUTE_NAME };
  let subject = this.subject({
    _latestTransition: transition
  });

  let isLeafRoute = subject.isLeafRoute();

  assert.ok(isLeafRoute, 'correctly identifies leaf route');
});

test('isLeafRoute - falsey w/ saved transition', function (assert) {
  let transition = { targetName: 'stuff.things' };
  let subject = this.subject({
    _latestTransition: transition
  });

  let isLeafRoute = subject.isLeafRoute();

  assert.notOk(isLeafRoute, 'correctly identifies non-leaf route');
});

test('sendTransitionBenchmark', function (assert) {
  let subject = this.subject();
  let benchmark = subject.get('tracking.benchmark');
  let stub = this.stub(benchmark, 'transition');
  let phase = 'nom';
  let targetName = 'bom';
  let lostVisibility = subject.get('documentVisibility.lostVisibility');
  let timestamp = 12345;

  subject.sendTransitionBenchmark(phase, targetName, timestamp);

  assert.ok(stub.calledOnce, 'transition was called on the benchmark library');

  let { args } = stub.firstCall;
  assert.equal(args[0], phase, 'event name passed');

  let data = args[1];
  assert.equal(data.destination, targetName, 'target name passed');
  assert.equal(data.route_name, ROUTE_NAME, 'route name passed');
  assert.equal(data.lost_visibility, lostVisibility, 'lost visibility status passed');
  assert.equal(data.client_time, timestamp, 'timestamp passed');
});

test('monitorInteractivity', function (assert) {
  assert.expect(6);

  let done = assert.async();

  let subject = this.subject({
    isInteractive() {}
  });
  let interactivity = subject.get('interactivity');

  let spy = this.spy(interactivity, 'monitor');

  subject.monitorInteractivity();

  assert.ok(spy.calledOnce, 'monitor was called');
  assert.ok(subject.get('monitoringInteractivity'), 'monitoring active');

  let { args } = spy.firstCall;
  assert.equal(typeof(args[0].isInteractive), 'function', 'isInteractive method passed');

  let stub = this.stub(subject, 'sendTransitionCompleteBenchmark');
  let timestamp = 12345;

  resolve(timestamp);

  return wait().then(() => {
    assert.notOk(subject.get('monitoringInteractivity'), 'monitoring inactive');
    assert.ok(stub.calledOnce, 'sendTransitionCompleteBenchmark called');
    assert.equal(stub.firstCall.args[0], timestamp, 'timestamp passed for benchmark');

    done();
  });
});

test('monitorInteractivity - not monitoring', function (assert) {
  assert.expect(1);

  let done = assert.async();

  let subject = this.subject();

  subject.monitorInteractivity();

  let stub = this.stub(subject, 'sendTransitionCompleteBenchmark');
  let timestamp = 12345;

  subject.set('monitoringInteractivity', false);
  resolve(timestamp);

  return wait().then(() => {
    assert.notOk(stub.calledOnce, 'sendTransitionCompleteBenchmark not called if monitoring is inactive');

    done();
  });
});

test('didTransition - not leaf route', function (assert) {
  let subject = this.subject();
  let stub = this.stub(subject, 'isLeafRoute', () => false);

  let result = subject.actions.didTransition.call(subject);

  assert.ok(stub.calledOnce, 'isLeafRoute was called');
  assert.ok(result, 'returns true unless _super is false');
});

test('didTransition - default', function (assert) {
  let stub = this.stub(run, 'scheduleOnce');

  let subject = this.subject();
  this.stub(subject, 'isLeafRoute', () => true);

  subject.actions.didTransition.call(subject);

  assert.ok(stub.calledOnce, 'scheduleOnce was called');
  let { args } = stub.firstCall;
  assert.equal(args[0], 'afterRender', 'afterRender scheduled');
  assert.equal(args[1], subject, 'correct context passed');
  assert.equal(args[2], subject.sendTransitionCompleteBenchmark, 'correct method passed');
});

test('didTransition - interactivity', function (assert) {
  let subject = this.subject({
    isInteractive() {}
  });
  this.stub(subject, 'isLeafRoute', () => true);

  let stub = this.stub(subject, 'monitorInteractivity');

  subject.actions.didTransition.call(subject);

  assert.ok(stub.calledOnce, 'monitorInteractivity was called');
});
