import { test } from 'ember-qunit';
import Mirage from 'ember-cli-mirage';
import wait from 'ember-test-helpers/wait';
import Service from 'ember-service';

import moduleForIntegration from 'web-client/tests/helpers/module-for-integration';
import ENV from 'web-client/config/environment';
import { pollTaskFor } from 'web-client/mixins/context-bound-tasks';
import { presenceHost, configurePubSub } from 'web-client/utilities/presence-util';

import PubsubDriver from 'pubsub-js-client/PubsubDriver';

moduleForIntegration('service:twitch-presence/backend/polling', 'Integration | Service | polling', {
  beforeEach() {
    let driver = PubsubDriver.getInstance(ENV.pubsub.environment);
    let presenceServiceStub = Service.extend({ sessionId: '12345' });
    let userIdleServiceStub = Service.extend({
      on() {},
      off() {}
    });

    configurePubSub(driver);

    /*
     * The Presence service kicks off polling on initialization. We stop
     * that from happening here so that we can test the Polling service in
     * relative isolation.
     */
    this.register('service:twitch-presence/presence', presenceServiceStub);

    /*
     * After initialization, the `idleChanged` event of the User Idle service
     * could cause the Ping service to make an additional presence call.
     * We stub the User Idle service here to test the Polling service in
     * relative isolation.
     */
    this.register('service:user-idle', userIdleServiceStub);
  }
});

test('#startPolling when not polling', function (assert) {
  assert.expect(3);

  let service = this.subject();

  this.server.post(`${presenceHost}/v2/online`, () => {
    assert.ok(true, 'polling occurs');
  });

  service.startPolling();

  assert.ok(service.get('isPolling'), 'isPolling is true');

  return wait().then(() => {
    try {
      pollTaskFor('TwitchPresenceBackendPollingService#startPolling');
    } catch (e) {
      /* Squash error if the polled task doesn't exists */
    }
    return wait();
  });
});

test(`#startPolling when already polling doesn't poll again`, function (assert) {
  assert.expect(3);

  let service = this.subject();

  this.server.post(`${presenceHost}/v2/online`, () => {
    assert.ok(true, 'precond - polling occurs');
  });

  service.startPolling();

  assert.ok(service.get('isPolling'), 'precond - isPolling is true');

  service.startPolling();

  assert.ok(service.get('isPolling'), 'isPolling is true');

  return wait();
});

test('#stopPolling', function (assert) {
  assert.expect(3);

  let service = this.subject();

  this.server.post(`${presenceHost}/v2/online`, () => {
    assert.ok(true, 'precond - polling occurs');
  });

  service.startPolling();

  assert.ok(service.get('isPolling'), 'precond - isPolling is true');

  service.stopPolling();

  assert.notOk(service.get('isPolling'), 'isPolling is false');

  return wait().then(() => {
    try {
      pollTaskFor('TwitchPresenceBackendPollingService#startPolling');
    } catch (e) {
      /* Squash error if the polled task doesn't exists */
    }
    return wait();
  });
});

test('stops polling on 404 error response', function (assert) {
  assert.expect(3);

  let service = this.subject();

  this.server.post(`${presenceHost}/v2/online`, () => {
    assert.ok(true, 'precond - polling occurs');

    return new Mirage.Response(404);
  });

  service.startPolling();

  assert.ok(service.get('isPolling'), 'precond - isPolling is true');

  return wait().then(() => {
    assert.notOk(service.get('isPolling'), 'isPolling is false due to 404');
  });
});

test('retries polling on 500 error response', function (assert) {
  assert.expect(3);

  let service = this.subject();

  this.server.post(`${presenceHost}/v2/online`, () => {
    assert.ok(true, 'precond - polling occurs');

    return new Mirage.Response(500);
  });

  service.startPolling();

  assert.ok(service.get('isPolling'), 'precond - isPolling is true');

  return wait().then(() => {
    assert.ok(service.get('isPolling'), 'isPolling is retried due to 500');
  });
});

test('polls with received custom interval on 204 response', function (assert) {
  assert.expect(6);

  let service = this.subject();
  let interval = 100;
  let tasks = [];

  this.server.post(`${presenceHost}/v2/online`, () => {
    assert.ok(true, 'precond - polling occurs');

    // interval from the server should be in seconds
    return new Mirage.Response(204, { 'Poll-Interval': interval / 1000 });
  });

  // Queue tasks from runTask
  service.runTask = (fn, time) => {
    tasks.push({ fn, time });
  };

  service.startPolling();

  assert.ok(service.get('isPolling'), 'precond - isPolling is true');

  return wait().then(() => {
    assert.equal(tasks.length, 1, 'added one task');

    let { fn, time } = tasks.pop();
    assert.equal(time, interval, 'task is scheduled for the time given by the server');

    // Run the task
    fn();
    pollTaskFor('TwitchPresenceBackendPollingService#startPolling');

    return wait();
  }).then(() => {
    assert.equal(tasks.length, 1, 'calling the task function adds another task');
  });
});
