import { module, test } from 'qunit';
import config from 'web-client/config/environment';
import VideoPoller from 'web-client/utilities/video-uploader/video-poller';

const POLL_TIME = config.delay.videoUploader.pollStatusInterval;

module('Unit | Utility | video-poller', {
  beforeEach() {
    this.tasks = [];
    this.videoPoller = new VideoPoller((fn, time) => {
      this.tasks.push({ fn, time });
    });
  }
});

test('pushing a video', function(assert) {
  let workItem = new FakeWorkItem();
  this.videoPoller.push(workItem);

  assert.equal(this.tasks.length, 1, "enqueues a task");

  let { fn, time } = this.tasks.pop();
  assert.equal(time, POLL_TIME, "enqueues with the POLL_TIME");
  assert.equal(workItem.reloadCount, 0, "not reloaded yet");

  fn();

  assert.equal(workItem.reloadCount, 1, "calls reload on the video");
  assert.equal(this.tasks.length, 1, "enqueues another task");
});

test('stops reloading a video once its status is settled', function(assert) {
  let workItem = new FakeWorkItem({ isStatusSettled: true });
  this.videoPoller.push(workItem);

  this.tasks.pop().fn();

  assert.equal(this.tasks.length, 0, "does not enqueue another task");
  assert.equal(workItem.reloadCount, 0, "did not call reload on the video");
});

test('stops reloading a video if it isDeleted', function(assert) {
  let workItem = new FakeWorkItem({ isDeleted: true });
  this.videoPoller.push(workItem);

  this.tasks.pop().fn();

  assert.equal(this.tasks.length, 0, "does not enqueue another task");
  assert.equal(workItem.reloadCount, 0, "did not call reload on the video");
});

test('stops reloading a video if it isUploading', function(assert) {
  let workItem = new FakeWorkItem({ isUploading: true });
  this.videoPoller.push(workItem);

  this.tasks.pop().fn();

  assert.equal(this.tasks.length, 0, "does not enqueue another task");
  assert.equal(workItem.reloadCount, 0, "did not call reload on the video");
});

test('does not reload a video if it is already inFlight', function(assert) {
  let workItem = new FakeWorkItem({ isInFlight: true });
  this.videoPoller.push(workItem);

  this.tasks.pop().fn();

  assert.equal(this.tasks.length, 1, "enqueues another task");
  assert.equal(workItem.reloadCount, 0, "did not call reload on the video");
});

test('#clear', function(assert) {
  let workItem = new FakeWorkItem();
  this.videoPoller.push(workItem);

  this.videoPoller.clear();
  this.tasks.pop().fn();

  assert.equal(this.tasks.length, 0, "does not enqueue another task");
  assert.equal(workItem.reloadCount, 0, "did not call reload on the video");
});

test('it does not add duplicate items', function(assert) {
  let workItem = new FakeWorkItem();
  this.videoPoller.push(workItem);
  this.videoPoller.push(workItem);

  this.tasks.pop().fn();

  assert.equal(workItem.reloadCount, 1, "only calls reload on the video once");
  assert.equal(this.tasks.length, 1, "enqueues another task");
});

test('can add an item that was previously in the list after clearing', function(assert) {
  let workItem = new FakeWorkItem();
  this.videoPoller.push(workItem);
  this.tasks.pop().fn();

  this.videoPoller.clear();

  this.videoPoller.push(workItem);
  this.tasks.pop().fn();

  assert.equal(workItem.reloadCount, 2, "reloaded the video twice");
});

test('can re-add an item that was previously dropped from the list', function(assert) {
  let workItem = new FakeWorkItem();
  this.videoPoller.push(workItem);

  // Work item becomes settled before the task runs.
  workItem.isStatusSettled = true;
  this.tasks.pop().fn();

  assert.equal(workItem.reloadCount, 0, "did not reload the video");
  assert.equal(this.tasks.length, 0, "did not enqueue another task");

  // Work item changes to be unsettled and is re-added
  workItem.isStatusSettled = false;
  this.videoPoller.push(workItem);

  assert.equal(this.tasks.length, 1, "enqueued another task");

  this.tasks.pop().fn();

  assert.equal(workItem.reloadCount, 1, "reloaded the video once");
});

test('rotates reloading workItems', function(assert) {
  let workItems = [new FakeWorkItem(), new FakeWorkItem()];

  this.videoPoller.push(...workItems);
  this.tasks.pop().fn();

  assert.deepEqual(
    workItems.map(w => w.reloadCount),
    [1, 0],
    'Reloads the first item'
  );

  this.tasks.pop().fn();

  assert.deepEqual(
    workItems.map(w => w.reloadCount),
    [1, 1],
    'Reloads the second item'
  );

  this.tasks.pop().fn();

  assert.deepEqual(
    workItems.map(w => w.reloadCount),
    [2, 1],
    'Reloads the first item again'
  );
});

let idCounter = 1;

class FakeWorkItem {
  constructor(attrs = {}) {
    this.reloadCount = 0;
    this.isStatusSettled = false;
    this.isDeleted = false;
    this.isInFlight = false;
    this.isUploading = false;
    this.lastReload = 0;
    this.id = ++idCounter;

    Object.keys(attrs).forEach((key) => {
      this[key] = attrs[key];
    });
  }

  reload(tickCount) {
    this.lastReload = tickCount;
    this.reloadCount++;
  }
}
