/* globals URI */

import { moduleForModel, test } from 'ember-qunit';
import {
  setup as setupMirage, teardown as teardownMirage
} from 'web-client/tests/helpers/setup-mirage-for-integration';
import run from 'ember-runloop';
import moment from 'moment';
import RSVP from 'rsvp';
import { copy } from 'ember-metal/utils';
import FakeFile from 'web-client/tests/helpers/fake-file';
import { UPLOAD_TYPE_PARAM } from 'web-client/utilities/video/types';

const fixture = {
  upload: {
    url: 'https://uploads.twitch.tv/upload/v123',
    token: 'some-json-token'
  },
  video: {
    title: 'Video Title',
    description: null,
    broadcast_type: UPLOAD_TYPE_PARAM,
    status: 'created',
    tag_list: '',
    _id: 'v123',
    game: 'StarCraft II',
    length: 0,
    viewable: 'public',
    viewable_at: null,
    preview: {
      small: 'https://www.twitch.tv/images/xarth/404_processing_80x45.png',
      medium: 'https://www.twitch.tv/images/xarth/404_processing_320x180.png',
      large: 'https://www.twitch.tv/images/xarth/404_processing_640x360.png',
      template: 'https://www.twitch.tv/images/xarth/404_processing_{width}x{height}.png'
    },
    thumbnails: {
      small: [],
      medium: [],
      large: [],
      template: []
    },
    created_at: '2016-07-07T13:10:40Z',
    _links: {
      self: 'http://api.twitch.tv/kraken/videos/v76358832',
      channel: 'http://api.twitch.tv/kraken/channels/%23%3COpenStruct%20id=37519064,%20login=%22golftest120%22,%20name=%22golftest120%22,%20display_name=%22GOLFtest120%22%3E'
    },
    channel: {
      name: 'golftest120',
      display_name: 'GOLFtest120'
    }
  }
};

moduleForModel('manager/uploader-video', 'Integration | Models | manager/uploader-video', {
  integration: true,

  beforeEach() {
    setupMirage(this);
  },

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

test('a video can be considered scheduled', function(assert) {
  assert.expect(1);

  let channelName = 'channelname';
  this.server.create('channel', { name: channelName });

  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName }).save();
  }).then((video) => {
    video.setProperties({
      viewable: 'private',
      viewableAt: Date.now()
    });
    assert.equal(video.get('isScheduled'), true, "isScheduled is true under the right conditions");
  });
});

test('a video can be considered unpublished', function(assert) {
  assert.expect(1);

  let channelName = 'channelname';
  this.server.create('channel', { name: channelName });

  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName }).save();
  }).then((video) => {
    video.setProperties({
      viewable: 'private',
      viewableAt: null
    });
    assert.equal(video.get('isUnpublished'), true, "isUnpublished is true under the right conditions");
  });
});

test('a video has recordedDate property', function(assert) {
  assert.expect(1);

  let channelName = 'channelname';
  this.server.create('channel', { name: channelName });

  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName }).save();
  }).then((video) => {
    video.setProperties({
      viewable: 'private',
      viewableAt: null
    });
    assert.equal(video.get('recordedDate'), video.get('createdAt'), "recordedDate is set to created_at for new unpublished uploads");
  });
});

test('creating an uploader video', function(assert) {
  assert.expect(14);

  this.server.post('https://api.twitch.tv/kraken/videos', function(schema, request) {
    assert.equal(request.queryParams.channel_id, 'channelname', "passed the channel name as a url param");
    assert.equal(request.requestHeaders.Accept, 'application/vnd.twitchtv.v4+json', "passed API version 4 accept headers");

    return fixture;
  });

  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName: 'channelname' }).save();
  }).then((video) => {
    assert.equal(video.get('id'), '123', 'serializes id');
    assert.equal(video.get('title'), 'Video Title', 'serializes title');
    assert.equal(video.get('game'), 'StarCraft II', 'serializes game');
    assert.strictEqual(video.get('length'), 0, 'serializes length as a number');
    assert.equal(video.get('status'), 'created', 'serializes status');

    assert.deepEqual(video.get('preview'), {}, 'removes 404 preview URLs');

    let createdAt = video.get('createdAt');
    assert.ok(createdAt instanceof Date, 'createdAt is deserialized as a Date object');
    assert.ok(moment(createdAt).isSame('2016-07-07T13:10:40Z'), 'serializes createdAt');

    assert.equal(video.get('channelName'), 'golftest120', 'serializes channel.name as channelName');

    let videoUpload = video.get('videoUpload');
    assert.ok(videoUpload, 'has an associated video upload');
    assert.equal(videoUpload.get('token'), 'some-json-token');
    assert.equal(videoUpload.get('url'), 'https://uploads.twitch.tv/upload/v123');
  });
});

// Unfortunately this test isn't quite aligned with how aborting an upload
// actually works due to a bug in pretenderjs/FakeXMLHttpRequest:
// https://github.com/pretenderjs/FakeXMLHttpRequest/issues/25
//
// In the test, the xhr `error` and `abort` events will fire, but in browsers,
// only the xhr `abort` event will fire.
test('deleting an uploader video', function(assert) {
  assert.expect(6);

  // Don't finish the upload until manually resolved in the test
  this.server.options.videoFileUploadTiming = true;

  let channelName = 'channelname';
  this.server.create('channel', { name: channelName });

  let request, videoUpload;
  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName }).save();
  }).then((video) => {
    let upload = video.get('videoUpload');
    upload.set('file', new FakeFile('dummy'));
    upload.save();

    return resolveNext(video);
  }).then((video) => {
    request = this.server.schema.videoUploads.first().request;

    videoUpload = video.get('videoUpload');
    assert.equal(videoUpload.get('isSaving'), true, "precondition - video upload isSaving");
    assert.equal(videoUpload.get('isComplete'), false, "precondition - video upload is not complete");
    assert.notOk(request.aborted, "precondition - request is not aborted yet");

    return video.destroyRecord();
  }).then(() => {
    assert.equal(request.aborted, true, "request was aborted");
    assert.equal(videoUpload.get('isComplete'), true, "video upload isComplete");
    assert.equal(videoUpload.get('isSaving'), false, "video upload is not saving");
    this.server.pretender.resolve(request);
  });
});

test('updating an uploader video', function(assert) {
  assert.expect(1);

  let channelName = 'channelname';
  this.server.create('channel', { name: channelName });

  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName }).save();
  }).then((video) => {
    return video.save();
  }).then((video) => {
    assert.equal(video.get('channelName'), channelName, "retains channelName even though it's missing from the payload");
  });
});

test("Deleting the custom thumbnail when there are no generated thumbnails", function(assert) {
  assert.expect(4);

  this.server.post('https://api.twitch.tv/kraken/videos', function() {
    let response = copy(fixture, true);

    // Setup data where there is one custom thumbnail
    response.video.thumbnails.medium = [{ url: 'custom.png', type: 'custom' }];
    response.video.preview = { medium: 'custom.png' };

    return response;
  });

  let updatePayload;
  this.server.put('https://api.twitch.tv/api/vods/123', function(schema, request) {
    updatePayload = URI.parseQuery(request.requestBody);
  });

  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName: 'dummy' }).save();
  }).then((video) => {
    assert.equal(video.get('thumbnailIndex'), 0, '`thumbnailIndex` begins at zero');

    video.removeCustomThumbnail();
    assert.equal(video.get('thumbnails.length'), 0, 'thumbnails are empty after removing custom thumbnail');

    return video.save();
  }).then(() => {
    assert.equal(updatePayload.delete_custom_thumbnail, "true", 'passed deletion flag to the server');
    assert.equal('thumbnail_index' in updatePayload, false, 'did not send thumbnail index to the server');
  });
});

test("Deleting the custom thumbnail when there is a generated thumbnail", function(assert) {
  assert.expect(6);

  this.server.post('https://api.twitch.tv/kraken/videos', function() {
    let response = copy(fixture, true);

    // Setup data where there is one selected custom thumbnail and one generated thumbnail
    response.video.thumbnails.medium = [
      { url: 'generated.png', type: 'generated' },
      { url: 'custom.png', type: 'custom' }
    ];
    response.video.preview = { medium: 'custom.png' };

    return response;
  });

  let updatePayload;
  this.server.put('https://api.twitch.tv/api/vods/123', function(schema, request) {
    updatePayload = URI.parseQuery(request.requestBody);
  });

  return run(() => {
    return this.store().createRecord('manager/uploader-video', { channelName: 'dummy' }).save();
  }).then((video) => {
    assert.equal(video.get('thumbnailIndex'), 1, '`thumbnailIndex` begins at 1');
    assert.equal(video.get('thumbnailPreviewUrl'), 'custom.png', "shows the custom thumbnail's url");

    video.removeCustomThumbnail();
    assert.equal(video.get('thumbnails.length'), 1, 'only one thumbnail remains');
    assert.equal(video.get('thumbnailPreviewUrl'), 'generated.png', "shows the generated thumbnail's url");

    return video.save();
  }).then(() => {
    assert.equal(updatePayload.delete_custom_thumbnail, "true", 'passed deletion flag to the server');
    assert.equal(updatePayload.thumbnail_index, "0", 'sent generated thumbnail index to the server');
  });
});

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