import { test } from 'qunit';
import sinon from 'sinon';
import moduleForAcceptance from 'web-client/tests/helpers/module-for-acceptance';
import VideoManagerUploadsPage from 'web-client/tests/pages/video-manager/uploads';
import videoAppealScenario from 'web-client/mirage/scenarios/video-appeal';
import { stubLogin } from 'web-client/tests/helpers/stub-login';
import supports from 'web-client/mirage/utils/supports';
import { UPLOAD_TYPE_PARAM } from 'web-client/utilities/video/types';

moduleForAcceptance('Acceptance | /manager/uploads', {
  beforeEach() {
    let username = 'mitchlloyd';
    stubLogin({ login: username });

    this.videoManagerProperties = this.server.create('video-manager-properties');
    this.channel = this.server.create('channel', { name: username });
    this.page = VideoManagerUploadsPage.create({ username });
    this.videoProcessor = this.owner.lookup('service:video-processor');
    this.notify = this.owner.lookup('service:notify');
    this.videoProcessor.isPaused = true;
  }
});

test('visiting the uploads page', function(assert) {
  assert.expect(2);
  visit('/mitchlloyd/manager/uploads');

  andThen(() => {
    assert.equal(currentURL(), this.page.url());
    assert.equal(currentRouteName(), 'manager.uploads', 'should load properly');
  });
});

test('uploading a video', function(assert) {
  assert.expect(16);
  let video;
  let content = 'file-content';
  let file = new Blob([content], {type : 'video/mp4'});
  let videoCard = this.page.inProgressVideoCard();

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

  visit(this.page.url());

  andThen(() => {
    assert.equal(currentURL(), this.page.url());
    assert.equal(videoCard.isPresent(), false, 'there is no video upload at first');
  });

  chooseFile(this.page.videoSelectionButton(), file);

  click('[data-test-upload-edit]');

  // Create video
  andThen(() => {
    assert.ok(videoCard.isPresent(), 'after choosing a file there is a video upload');
    assert.equal(videoCard.status(), 'uploading', 'card is in uploading status');

    video = this.server.schema.videos.first();
    video.videoUpload.request.upload.onprogress({ loaded: 4, total: content.length });
    assert.equal(video.videoUpload.file.size, content.length, 'file was sent');
  });

  // See upload progress
  andThen(() => {
    assert.equal(videoCard.uploadProgress(), '33%', 'shows upload progress');
  });

  // Submit video attributes
  fillIn(this.page.titleField(), 'Video Title');
  fillIn(this.page.descriptionField(), 'Video Description');
  fillIn(this.page.languageField(), 'en');
  fillIn(this.page.gameField(), 'League of Legends');
  fillIn(this.page.tagList(), 'tag1, tag2');
  fillIn(this.page.publishStrategiesSelect(), 'public');

  click(this.page.saveVideoButton());

  andThen(() => {
    video.reload();
    assert.equal(video.title, 'Video Title', 'updated title');
    assert.equal(video.description, 'Video Description', 'updated description');
    assert.equal(video.language, 'en', 'updated language');
    assert.equal(video.game, 'League of Legends', 'updated game');
    assert.equal(video.tag_list, 'tag1, tag2', 'updated tagList');
    assert.equal(video.viewable, 'public', 'updated publish strategy');

    this.server.pretender.resolve(video.videoUpload.request);
  });

  // Begin polling for status
  andThen(() => {
    assert.equal(video.videoUpload.isComplete, true, 'server has been told that the upload is complete');
    assert.equal(videoCard.status(), 'transcoding', 'waiting for transcoding after upload is complete');

    video.update({ status: 'transcoding' });
    this.videoProcessor.flushTaskBacklog();
  });

  andThen(() => {
    assert.equal(videoCard.status(), 'transcoding', 'status shows transcoding began');

    video.update({ status: 'recorded' });
    videoAppealScenario(this.server, video.id);
    this.videoProcessor.flushTaskBacklog();
  });

  andThen(() => {
    videoCard = this.page.firstPublishedVideoCard();

    assert.equal(videoCard.status(), 'processed', 'status finalized as processed');
  });
});

test('uploading a thumbnail for a video', function(assert) {
  let assertionCount = supports.formDataGet ? 4 : 2;
  assert.expect(assertionCount);

  let video = this.server.create('video', {
    broadcast_type: UPLOAD_TYPE_PARAM,
    channel: this.channel,
    id: 1
  });
  videoAppealScenario(this.server, video.id);

  let imageFile = new Blob(['123'], { type: 'image/png' });
  let videoCard = this.page.firstPublishedVideoCard();

  // Stub out loadImage library to produce canvas.
  sinon.stub(window, 'loadImage', function(file, callback) {
    callback(document.createElement('canvas'));
  });

  visit(this.page.url());

  andThen(() => {
    assert.equal(videoCard.isPresent(), true, 'there is a published video card');
  });

  click(videoCard.editButtonSelector());
  chooseFile(this.page.thumbnailUploadButton(), imageFile);

  click(this.page.saveButton());

  andThen(() => {
    let thumbnail = video.reload().customThumbnail;
    assert.ok(thumbnail, 'the video has a thumbnail');

    if (supports.formDataGet) {
      assert.equal(thumbnail.image.size, 3, 'custom thumbnail has the image');
      assert.equal(videoCard.thumbnailSrc(), 'https://static-cdn.jtvnw.net/s3_vods/1/thumb/0-0-152-270-3.jpeg', 'video card is showing the custom thumbnail');
    }

    window.loadImage.restore();
  });
});

test('initiating an upload with drag and drop', function(assert) {
  assert.expect(4);
  let file = new Blob(['file-content'], { type: 'video/mp4' });
  let videoCard = this.page.inProgressVideoCard();

  visit(this.page.url());

  andThen(() => {
    assert.equal(currentURL(), this.page.url());
    assert.equal(videoCard.isPresent(), false, 'there is no video upload at first');
  });

  triggerEvent('[data-test-file-drop-zone]', 'drop', { dataTransfer: { files: [file] } });

  andThen(() => {
    assert.ok(videoCard.isPresent(), 'after choosing a file there is a video upload');
    assert.equal(videoCard.status(), 'uploading', 'card is in uploading status');
  });
});

test('deleting an upload', function(assert) {
  assert.expect(2);

  let video = this.server.create('video', {
    broadcast_type: UPLOAD_TYPE_PARAM,
    channel: this.channel,
    id: 1
  });
  videoAppealScenario(this.server, video.id);

  let videoCard = this.page.firstPublishedVideoCard();

  visit(this.page.url());
  click(videoCard.moreMenuButton());
  click(videoCard.deleteButton());
  click(this.page.confirmButtonSelector);

  andThen(() => {
    assert.equal(videoCard.isPresent(), false, 'there are no published videos');
    assert.equal(this.server.schema.videos.find(video.id), null, 'video has been deleted from the server');
  });
});

test('resuming polling when returning to uploads', function(assert) {
  assert.expect(2);

  let video = this.server.create('video', {
    broadcast_type: UPLOAD_TYPE_PARAM,
    channel: this.channel,
    status: 'transcoding',
    id: 1
  });

  let videoCard = this.page.inProgressVideoCard();

  visit(this.page.url());

  andThen(() => {
    assert.equal(videoCard.status(), 'transcoding', 'the video is transcoding');

    video.update({ status: 'recorded' });
    videoAppealScenario(this.server, video.id);
    this.videoProcessor.flushTaskBacklog();
  });

  andThen(() => {
    assert.ok(this.page.firstPublishedVideoCard().isPresent(), 'the video is finished');
  });
});

test('warning users when leaving the video manager while uploads are pending', function(assert) {
  assert.expect(4);
  let file = new Blob(['file-content'], {type : 'video/mp4'});

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

  visit(this.page.url());
  chooseFile(this.page.videoSelectionButton(), file);

  andThen(() => {
    click(this.page.uploadVideoButton());
  });

  visit('mitchlloyd/manager/highlights');

  andThen(() => {
    assert.notOk(this.page.hasPendingUploadsWarning(), "user sees no warning navigating within the video manager");
  });

  visit('/');

  andThen(() => {
    assert.ok(this.page.hasPendingUploadsWarning(), "shows the user a warning about pending uploads");
  });

  // First the user decides not to leave
  click(this.page.cancelButtonSelector);

  andThen(() => {
    assert.equal(currentPath(), 'user.manager.highlights');
  });

  visit('/');

  // Then the user decides to continue
  click(this.page.confirmButtonSelector);

  andThen(() => {
    assert.equal(currentPath(), 'index');
    let request = this.server.schema.videos.first().videoUpload.request;
    this.server.pretender.resolve(request);
  });
});

test('leaving shows no warning after uploads are finished', function(assert) {
  assert.expect(1);
  let file = new Blob(['file-content'], {type : 'video/mp4'});

  visit(this.page.url());
  chooseFile(this.page.videoSelectionButton(), file);

  andThen(() => {
    find(this.page.publishStrategiesSelect()).val('scheduled').trigger('change');
  });

  andThen(() => {
    click(this.page.uploadVideoButton());
  });

  // Resolve the upload request
  andThen(() => {
    let request = this.server.schema.videos.first().videoUpload.request;
    this.server.pretender.resolve(request);
  });

  visit('/');

  andThen(() => {
    assert.equal(currentPath(), 'index');
  });
});

test('querying videos', function(assert) {
  assert.expect(1);

  ['pending_transcode', 'transcoding', 'uploading', 'failed', 'created', 'recorded'].forEach((status) => {
    this.server.create('video', {
      broadcast_type: UPLOAD_TYPE_PARAM,
      channel: this.channel,
      status: status,
      title: `${status} video`
    });
    videoAppealScenario(this.server, 1);
  });

  visit(this.page.url());

  andThen(() => {
    assert.deepEqual(
      this.page.videoTitles(),
      ['failed video', 'transcoding video', 'pending_transcode video', 'recorded video'],
      'omits "created" and "uploading" videos'
    );
  });
});

test('handling an error when fetching video uploads', function(assert) {
  this.server.get('http://api.twitch.tv/api/channels/mitchlloyd/video_manager/uploads', {}, 500);

  visit(this.page.url());

  andThen(() => {
    assert.stringIncludes(this.page.fetchVideosErrorMessage(), "problem");
  });
});

test('visiting uploads tab when email is not verified', function(assert) {
  assert.expect(1);

  this.videoManagerProperties.update('email_verified', false);

  visit(this.page.url());

  andThen(() => {
    assert.ok(this.page.hasMustVerifyAccountNotice(), 'notifies user that they must verify their account');
  });
});

test('when a video is muted and making an appeal', function(assert) {
  assert.expect(1);

  let video = this.server.create('video', {
    broadcast_type: UPLOAD_TYPE_PARAM,
    channel: this.channel,
    id: 1,
    is_muted: true,
    status: 'recorded'
  });

  videoAppealScenario(this.server, video.id);

  let videoCard = this.page.firstPublishedVideoCard();

  visit(this.page.url());
  click(videoCard.moreMenuButton());
  click(`[data-test-appeal-button]`);

  andThen(() => {
    let appealModal = find('[data-test-appeal-modal]');
    assert.equal(appealModal.length, 1, 'should open the appeal modal');
  });
});

test('video cards have the expected buttons and menu options showing', function(assert) {
  assert.expect(5);

  this.server.create('video', {
    broadcast_type: UPLOAD_TYPE_PARAM,
    channel: this.channel,
    id: 1,
    is_muted: false,
    status: 'recorded'
  });

  visit(this.page.url());
  andThen(() => {
    let editButton = find('[data-test-selector=edit-button]');
    let moreMenu = find('[data-test-selector=more-menu]');
    let moreButton = moreMenu.find('[data-test-selector=more-menu-button]');
    let deleteButton = moreMenu.find('[data-test-selector=delete-button]');
    let videoStatsButton = moreMenu.find('[data-test-selector=video-stats-button]');

    assert.equal(editButton.length, 1, 'Edit button present');
    assert.equal(moreMenu.length, 1, 'More menu present');
    assert.equal(moreButton.length, 1, 'More button present');
    assert.equal(deleteButton.length, 1, 'More menu should have delete button');
    assert.equal(videoStatsButton.length, 1, 'More menu should have video stats button');
  });
});
