/* globals sendIRCMessage */
import { test, skip } from 'qunit';
import moduleForAcceptance from 'web-client/tests/helpers/module-for-acceptance';
import ChannelPage from 'web-client/tests/pages/channel/redesign';
import VideoPage from 'web-client/tests/pages/videos';
import channelScenario from 'web-client/mirage/scenarios/channel';
import videoScenario from 'web-client/mirage/scenarios/video';
import { lastPlayerInstance } from 'web-client/tests/helpers/fake-player';

const DEFAULT_ANIMATION_STYLE = 'all 0s ease 0s';
const CHANNEL_NAME = 'twitch';

function setupChannelPage(context) {
  context.channel = channelScenario(context.server, CHANNEL_NAME);
  context.page = ChannelPage.create({ name: context.channel.name });

  context.server.createList('post', 10);
}

function setupOfflineChannelPage(context) {
  context.channel = channelScenario(context.server, CHANNEL_NAME, { isOffline: true });
  context.page = ChannelPage.create({ name: context.channel.name });

  context.server.createList('post', 10);
}

function setupVODPage (context, presetChannel) {
  let channel;

  if (!presetChannel) {
    channel = channelScenario(context.server, CHANNEL_NAME);
  } else {
    channel = presetChannel;
  }

  context.video = videoScenario(context.server, {
    channel: channel
  });

  context.page = VideoPage.create({
    vodId: context.video.id
  });
}

//  Kind of a hack. A more realistic scenario would be to simulate the
//  existence of a related VOD, a recent VOD from the same streamer, and
//  a popular VOD from the same game, but that requires a lot of test
//  infrastructure that doesn't exist
function makePageScrollable(page) {
  let scrollContainer = page.scrollContainer();
  scrollContainer.append('<div style="width: 100%; height: 2000px;"></div>');
}

function setTestingContainerFullsize() {
  // position the testing container as fixed so that ts
  // border matches the window's
  $('#ember-testing-container').css({
    position: 'fixed',
    width: '100%',
    height: '100%',
    overflow: 'auto',
    border: 'none'
  });

  $('#ember-testing').css({
    width: '100%',
    zoom: '100%'
  });
}

function resetTestingContainer() {
  $('#ember-testing-container').css({
    position: 'relative',
    width: '',
    height: '',
    overflow: '',
    border: ''
  });

  $('#ember-testing').css({
    width: '',
    zoom: ''
  });
}

moduleForAcceptance('Acceptance | persistent-player', {
  beforeEach() {
    this.experiments.use({
      DETERMINATION: 'yes',
      CHANNEL_PAGE_REDESIGN: 'new'
    });

    setTestingContainerFullsize();
  },
  afterEach() {
    this.experiments.use({
      DETERMINATION: 'no',
      CHANNEL_PAGE_REDESIGN: 'old'
    });

    resetTestingContainer();
  }
});

test('when on an online channel page', function(assert) {
  assert.expect(3);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    let placeholder = find(this.page.placeholder);

    assert.equal(player.length, 1, 'should render the element');
    assert.equal(placeholder.length, 1, 'should render the placeholder');
    assert.propEqual(player.offset(), placeholder.offset(), 'should match the position of the placeholder');
  });
});

test('when scrolling on an online channel page', function(assert) {
  assert.expect(3);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    this.page.scrollDown();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'should finish animating and become mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });

    this.page.scrollUp();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'should become full-size');
    });
  });
});

test('when scrolling on an offline channel page', function(assert) {
  assert.expect(1);

  setupOfflineChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    this.page.scrollDown();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'should not become mini');
    });
  });
});

test('when clicking the `X` button on the channel page', function(assert) {
  assert.expect(5);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    makePageScrollable(this.page);
    this.page.scroll(10000);
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'));
    });

    click('.js-player-mini__titlebar .js-close-button');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'should return to original position when the X is clicked');
      assert.notOk(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container does not have padding for the player');
    });

    this.page.scroll(5000);

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'should not shrink again until the user scrolls back to the top');
    });

    this.page.scroll(0);
    triggerEvent(player, 'transitionend');
    this.page.scroll(10000);
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'should be able to shrink after the user has scrolled back to the top');
    });
  });
});

test('when on a VOD page', function(assert) {
  assert.expect(3);

  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    let placeholder = find(this.page.placeholder);

    assert.equal(player.length, 1, 'should render the element');
    assert.equal(placeholder.length, 1, 'should render the placeholder');
    assert.propEqual(player.offset(), placeholder.offset(), 'should match the position of the placeholder');
  });
});

test("VOD page tracks page latency by reporting when it's ready to accept user interaction", function(assert) {
  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    // Invoke the player's "contentShowing" event to trigger its "reportInteractive" method
    lastPlayerInstance.listeners['contentShowing'].forEach(function (listener) {
      listener();
    });

    assert.trackInteractivity('videos');
  });

  andThen(() => {
    assert.trackBenchmarkEvent('complete_transition');
  });
});

// These two tests are flaky for undeterminable reasons
// The desired behavior is implemented in channel-redesign/component.js
// skipping for the time being, but definitely revisit when the /live and /vod
// routes are untangled from that class
skip('when scrolling on a VOD page', function(assert) {
  assert.expect(2);

  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    makePageScrollable(this.page);
    this.page.scrollDown();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'should finish animating and become mini');
    });

    this.page.scrollUp();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'should become full-size');
    });
  });
});

skip('when clicking the `X` button on the VOD page', function(assert) {
  assert.expect(4);

  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    makePageScrollable(this.page);
    this.page.scrollDown();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'));
      return click('.js-player-mini__titlebar .js-close-button');
    });

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'should return to original position when the X is clicked');
      assert.notOk(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container does not have padding for the player');
    });

    let scrollContainer = this.page.scrollContainer();
    scrollContainer.scrollTop(scrollContainer.scrollTop() - 1);
    triggerEvent(scrollContainer, 'scroll');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'should not shrink again until the user scrolls back to the top');
    });

    this.page.scrollUp();
    this.page.scrollDown();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'should be able to shrink after the user has scrolled back to the top');
    });
  });
});

test('when navigating from an online channel page to the videos page', function(assert) {
  assert.expect(2);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.videosButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when navigating from an online channel page to the followers page', function(assert) {
  assert.expect(2);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.followersButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when navigating from an online channel page to the following page', function(assert) {
  assert.expect(2);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.followingButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when navigating from an online channel page to the directory page', function(assert) {
  assert.expect(2);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when clicking the `X` on a mini player page', function(assert) {
  assert.expect(2);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      return click('.js-player-mini__titlebar .js-close-button');
    });

    andThen(() => {
      assert.ok(player.hasClass('hidden'), 'the persistent player is hidden');
      assert.notOk(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container does not have padding for the player');
    });
  });
});

test('when navigating from an offline channel page to a non-player page', function(assert) {
  assert.expect(3);

  setupOfflineChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'the player does not become mini');
      assert.ok(player.hasClass('hidden'), 'the player should not be displayed');
      assert.notOk(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container does not have padding for the player');
    });
  });
});

test('when navigating from the VOD page to the videos page', function(assert) {
  assert.expect(2);

  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.videosButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when navigating from the VOD page to the followers page', function(assert) {
  assert.expect(2);

  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.followersButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when navigating from the VOD page to the following page', function(assert) {
  assert.expect(2);

  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.followingButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when navigating from the VOD page to the directory page', function(assert) {
  assert.expect(2);

  setupVODPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      assert.ok(this.page.scrollContainer().hasClass('has-player-mini'), 'the scroll container has padding for the player');
    });
  });
});

test('when navigating from a VOD page to a miniplayer page when the channel is offline', function(assert) {
  assert.expect(1);
  this.channel = channelScenario(this.server, CHANNEL_NAME, { isOffline: true });
  setupVODPage(this, this.channel);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
    });
  });
});

test('when navigating from the videos page to the channel page with the miniplayer closed', function(assert) {
  assert.expect(4);

  setupChannelPage(this);
  visit(`${this.page.url()}/videos/all`);

  andThen(() => {
    assert.ok(this.page.persistentPlayer().hasClass('hidden'), 'the player should not initially be present');
  });

  click(this.page.profileButton);

  andThen(() => {
    let player = this.page.persistentPlayer();
    assert.notOk(player.hasClass('hidden'), 'the player should appear');
    assert.notOk(player.hasClass('js-player-mini'), 'the player should be full-size');
    assert.equal(player.css('transition'), DEFAULT_ANIMATION_STYLE, 'the player should not be animating');
  });
});

test('when navigating from the videos page to the VOD page with the miniplayer closed', function(assert) {
  assert.expect(4);

  setupChannelPage(this);
  let video = videoScenario(this.server, {
    channel: this.channel
  });

  visit(`${this.page.url()}/videos/all`);

  andThen(() => {
    assert.ok(this.page.persistentPlayer().hasClass('hidden'), 'the player should not initially be present');
  });

  click(this.page.video(video.title));

  andThen(() => {
    let player = this.page.persistentPlayer();
    assert.notOk(player.hasClass('hidden'), 'the player should appear');
    assert.notOk(player.hasClass('js-player-mini'), 'the player should be full-size');
    assert.equal(player.css('transition'), DEFAULT_ANIMATION_STYLE, 'the player should not be animating');
  });
});

test('when navigating from the videos page to the channel page with the miniplayer open', function(assert) {
  assert.expect(2);
  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.videosButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      return click(this.page.profileButton);
    });

    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'the player should be full-size');
    });
  });
});

test('when navigating from the videos page to the VOD page with the miniplayer open', function(assert) {
  assert.expect(2);
  setupChannelPage(this);
  let video = videoScenario(this.server, {
    channel: this.channel
  });
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find(this.page.videosButton));
    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.ok(player.hasClass('js-player-mini'), 'the player becomes mini');
      return click(this.page.video(video.title));
    });

    triggerEvent(player, 'transitionend');

    andThen(() => {
      assert.notOk(player.hasClass('js-player-mini'), 'the player should be full-size');
    });
  });
});

test('when clicking the expand button when the content is a live stream', function(assert) {
  assert.expect(1);
  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');
    andThen(() => {
      return click(find('.js-player-mini__expand-channel'));
    });

    andThen(() => {
      assert.equal(currentURL(), '/twitch', 'should link to the owner\'s channel page');
    });
  });
});

test('when clicking the expand button when the content is hosted', function(assert) {
  assert.expect(1);
  setupChannelPage(this);

  let hostedChannel = channelScenario(this.server, 'foilstormcrow');
  this.channel.hostModeTarget = hostedChannel;

  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');
    andThen(() => {
      return click(find('.js-player-mini__expand-channel'));
    });

    andThen(() => {
      assert.equal(currentURL(), '/twitch', 'should link to the host\'s channel page');
    });
  });
});

test('when clicking the expand button when the content is a VOD', function(assert) {
  assert.expect(1);
  setupVODPage(this);

  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');
    andThen(() => {
      return click(find('.js-player-mini__expand-channel'));
    });

    andThen(() => {
      assert.equal(currentURL(), `/videos/${this.video.id}`, 'should link to the video page');
    });
  });
});

test('when clicking the channel link when the content is hosted', function(assert) {
  assert.expect(1);
  setupChannelPage(this);

  let hostedChannel = channelScenario(this.server, 'foilstormcrow');

  visit(this.page.url());

  sendIRCMessage({
    command: 'HOSTTARGET',
    hostTarget: this.channel.name,
    target: hostedChannel.name
  });

  andThen(() => {
    let player = this.page.persistentPlayer();
    click(find('a[data-tt_content="directory_games"]'));
    triggerEvent(player, 'transitionend');
    andThen(() => {
      return click(find('.js-player-mini__channel'));
    });

    andThen(() => {
      assert.equal(currentURL(), '/foilstormcrow', 'should link to the hosted channel page');
    });
  });
});

test('when clicking the expand button while on the channel page', function(assert) {
  assert.expect(1);

  setupChannelPage(this);
  visit(this.page.url());

  andThen(() => {
    let player = this.page.persistentPlayer();
    this.page.scrollDown();
    triggerEvent(player, 'transitionend');

    andThen(() => {
      return click(find('.js-player-mini__expand-channel'));
    });

    triggerEvent(this.page.scrollContainer(), 'transitionend');

    andThen(() => {
      let banner = $('.js-cn-cover');
      assert.equal(Math.round(-banner.offset().top), Math.round(banner.height()), 'should scroll to the default position');
    });
  });
});
