import { moduleForModel, test } from 'ember-qunit';
import {
  setup as setupMirage, teardown as teardownMirage
} from 'web-client/tests/helpers/setup-mirage-for-integration';
import { Response } from 'ember-cli-mirage';
import run from 'ember-runloop';
import RSVP from 'rsvp';
import FakeFile from 'web-client/tests/helpers/fake-file';

moduleForModel('video-upload', 'Integration | Models | video-upload', {
  integration: true,

  beforeEach() {
    setupMirage(this);
    this.channel = this.server.create('channel');
  },

  afterEach() {
    teardownMirage(this);
  }
});

test('successfully uploading a videoUpload', function(assert) {
  assert.expect(9);
  let store = this.store();
  let apiCalls = [];

  // Must be manually resolved with Pretender
  this.server.put('https://uploads.twitch.tv/upload/v1', function(schema, request) {
    apiCalls.push({ name: 'upload', request });
    return new Response(200, {}, {});
  }, { timing: true });

  this.server.post('https://uploads.twitch.tv/upload/v1/complete', function(schema, request) {
    apiCalls.push({ name: 'complete', request });
    return new Response(200, {}, {});
  });

  let videoUpload, videoUploadSave;
  return run(() => {
    // Create a uploaderVideo record with an associated videoUpload
    let video = store.createRecord('manager/uploader-video', { channelName: this.channel.name });
    return video.save();
  }).then((video) => {
    videoUpload = video.get('videoUpload');
    videoUpload.set('file', new FakeFile('dummy'));

    // Begin the upload
    videoUploadSave = videoUpload.save();

    return resolveNext();
  }).then(() => {
    let { name, request } = apiCalls.pop();

    // Update the progress for the upload
    request.upload.onprogress({ loaded: 2, total: 12345 });
    assert.equal(videoUpload.get('total'), 5, 'model reflects total size of file (not event total)');
    assert.equal(videoUpload.get('loaded'), 2, 'model reflects amount loaded');
    assert.equal(videoUpload.get('isComplete'), false, 'model is not complete');

    // Assert on the upload request
    assert.equal(name, 'upload', 'hit the upload endpoint');
    assert.equal(request.queryParams.part, '1', 'adapter sends part=1 in the params');
    assert.equal(request.queryParams.upload_token, 'some-json-token', 'adapter sends json token in query params');

    // Finish the upload request
    this.server.pretender.resolve(request);

    // Wait for the upload request to finish on the client
    return videoUploadSave;
  }).then(() => {
    // Assert on the upload complete request
    let { name, request } = apiCalls.pop();
    assert.equal(name, 'complete', 'hit the upload endpoint');
    assert.equal(request.queryParams.upload_token, 'some-json-token', 'adapter sends json token in query params');
    assert.equal(videoUpload.get('isComplete'), true, 'model isComplete');
  });
});

test('handling an error durring the xhr upload', function(assert) {
  assert.expect(2);
  let store = this.store();
  let uploadRequest;

  // Must be manually resolved with Pretender
  this.server.put('https://uploads.twitch.tv/upload/v1', function(schema, request) {
    uploadRequest = request;
    return new Response(200, {}, {});
  }, { timing: true });

  let videoUploadSave;
  return run(() => {
    // Create an uploaderVideo record with an associated videoUpload
    return store.createRecord('manager/uploader-video', { channelName: this.channel.name }).save();
  }).then((video) => {
    let videoUpload = video.get('videoUpload');
    videoUpload.set('file', new FakeFile('dummy'));

    // Begin the upload
    videoUploadSave = videoUpload.save().then(undefined, () => {
      videoUploadSave.isRejected = true;
    });

    return resolveNext(video);
  }).then((video) => {
    // Upload fails with an unrecoverable error
    uploadRequest.onerror({ status: 400 });

    assert.equal(video.get('videoUpload.isError'), true, 'video upload isError is true');
    assert.equal(videoUploadSave.isRejected, true, 'video upload save rejected');
  });
});

test('handling an error on PUT to upload endpoint', function(assert) {
  assert.expect(2);
  let store = this.store();

  this.server.put('https://uploads.twitch.tv/upload/v1', {}, 500);
  this.server.post('https://uploads.twitch.tv/upload/v1/complete', () => {
    assert.notOk('true', "should not call complete endpoint");
  });

  return run(() => {
    // Create an uploaderVideo record with an associated videoUpload
    return store.createRecord('manager/uploader-video', { channelName: this.channel.name }).save();
  }).then((video) => {
    return video.get('videoUpload').save().catch(() => {
      assert.ok(true, "video save was rejected");
      assert.equal(video.get('videoUpload.isError'), true, 'video upload isError is true');
    });
  });
});

test('handling an error on POST to complete endpoint', function(assert) {
  assert.expect(2);
  let store = this.store();

  this.server.put('https://uploads.twitch.tv/upload/v1', {}, 200);
  this.server.post('https://uploads.twitch.tv/upload/v1/complete', {}, 500);

  return run(() => {
    // Create an uploaderVideo record with an associated videoUpload
    return store.createRecord('manager/uploader-video', { channelName: this.channel.name }).save();
  }).then((video) => {
    return video.get('videoUpload').save().catch(() => {
      assert.ok(true, "video save was rejected");
      assert.equal(video.get('videoUpload.isError'), true, 'video upload isError is true');
    });
  });
});


function resolveNext(value) {
  return new RSVP.Promise(function(resolve) {
    run.next(() => resolve(value));
  });
}
