import { test } from 'ember-qunit';
import moduleForComponent from 'web-client/tests/helpers/module-for-component';
import hbs from 'htmlbars-inline-precompile';
import wait from 'ember-test-helpers/wait';
import dispatchKeyEvent from 'web-client/tests/helpers/dispatch-key-event';

function largeSuggestions() {
  return [
    {id: 'iamauser', displayName: 'iamauser', whispered: true, timestamp: 1},
    {id: 'atest1', displayName: 'aTest1'},
    {id: 'atest2', displayName: 'atest2', timestamp: 4},
    {id: 'atest3', displayName: 'atest3', timestamp: 5},
    {id: 'atest4', displayName: 'atest4', timestamp: 6},
    {id: 'atest5', displayName: 'atest5'},
    {id: 'atest6', displayName: 'atest6'},
    {id: 'btest4', displayName: 'btest4', whispered: true, timestamp: 3},
    {id: 'btest5', displayName: 'btest5'},
    {id: 'btest6', displayName: 'btest6'},
    {id: 'ctest7', displayName: 'ctest7'},
    {id: 'ctest8', displayName: 'ctest8'},
    {id: 'ctest9', displayName: 'ctest9', whispered: true, timestamp: 2}
  ];
}

moduleForComponent('chat/twitch-chat-input', 'TwitchChatInputComponent', {
  beforeEach() {
    if (this.get('sortProperties') === undefined) {
      this.set('sortProperties', ['id']);
    }
    if (this.get('suggestions') === undefined) {
      this.set('suggestions', () => []);
    }
    this.renderTemplate = () => {
      this.render(hbs`
        {{chat/twitch-chat-input
            suggestions=suggestions
            textareaValue=textareaValue
            isTextareaDisabled=false
            placeholder='Send a message'
            numberOfSuggestionsToDisplay=5
            track=track
            sortProperties=sortProperties}}
      `);
      let textarea = this.$('textarea');
      let caretPosition = textarea.val().length;
      textarea[0].setSelectionRange(caretPosition, caretPosition);
    };
  }
});

test("Suggestions not shown if triggered when text area is empty", function (assert) {
  assert.expect(1);

  this.renderTemplate();

  dispatchKeyEvent('keydown', {
    target: this.$('textarea.mousetrap'),
    keyCode: 9
  });

  return wait().then(() => {
    assert.elementCount($('.suggestion'), 0, 'no suggestions');
  });
});

test("Shows 'No matches' if no suggestions are present", function (assert) {
  assert.expect(1);

  this.set('textareaValue', 'non-empty-input');
  this.renderTemplate();

  dispatchKeyEvent('keydown', {
    target: this.$('textarea.mousetrap'),
    keyCode: 9
  });

  return wait().then(() => {
    assert.elementText($('.suggestion'), 'No matches.', 'no matches UI displayed');
  });
});

test("Shows 'No matches' if no suggestions match input", function (assert) {
  assert.expect(1);

  this.set('textareaValue', 'invalidname');
  this.set('suggestions', largeSuggestions);
  this.renderTemplate();

  dispatchKeyEvent('keydown', {
    target: this.$('textarea.mousetrap'),
    keyCode: 9
  });

  return wait().then(() => {
    assert.elementText($('.suggestion'), 'No matches.', 'no matches UI displayed');
  });
});

test("Five suggestions are shown if more than five suggestions are available", function (assert) {
  assert.expect(1);

  this.set('textareaValue', 'atest');
  this.set('suggestions', largeSuggestions);
  this.renderTemplate();

  dispatchKeyEvent('keydown', {
    target: this.$('textarea.mousetrap'),
    keyCode: 9
  });

  return wait().then(() => {
    let output = $('.suggestion').map((idx, element) => $(element).text().trim()).get();
    let expected = largeSuggestions().sortBy('displayName').mapBy('displayName').slice(0, 5);
    assert.deepEqual(output, expected);
  });
});

test("Can click outside of suggestions to exit out of suggestions", function (assert) {
  assert.expect(1);

  this.set('textareaValue', 'atest');
  this.set('suggestions', largeSuggestions);
  this.renderTemplate();

  dispatchKeyEvent('keydown', {
    target: this.$('textarea.mousetrap'),
    keyCode: 9
  });

  return wait().then(() => {
    $('body').mouseup();
    return wait();
  }).then(() => {
    assert.elementCount(this.$('.suggestions'), 0, 'no suggestions present after outside click');
  });
});

test("Can use arrow keys to navigate suggestions", function (assert) {
  assert.expect(1);

  this.set('textareaValue', 'atest');
  this.set('suggestions', largeSuggestions);
  this.renderTemplate();

  let textarea = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', {
    target: textarea,
    keyCode: 9
  });

  return wait().then(() => {
    dispatchKeyEvent('keydown', {
      target: textarea,
      keyCode: 40
    });
    return wait();
  }).then(() => {
    assert.elementText(this.$('.highlighted'), 'atest2', 'arrow key highlights suggestion');
  });
});

test("Can send a tracking action when selecting suggestion", function (assert) {
  assert.expect(1);

  let didTrack = false;
  this.set('track', () => {
    didTrack = true;
  });
  this.set('textareaValue', 'atest');

  this.renderTemplate();

  let textarea = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', {
    target: textarea,
    keyCode: 9
  });

  return wait().then(() => {
    dispatchKeyEvent('keydown', {
      target: textarea,
      keyCode: 9
    });
    return wait();
  }).then(() => {
    assert.ok(didTrack, 'Tracking action was sent');
  });
});

test("Can use @ to initiate suggestions", function (assert) {
  assert.expect(1);

  this.set('suggestions', largeSuggestions);
  this.renderTemplate();

  dispatchKeyEvent('keydown', {
    target: this.$('textarea.mousetrap'),
    keyCode: 50,
    shiftKey: true
  });

  return wait().then(() => {
    assert.elementText(this.$('.highlighted'), "aTest1");
  });
});

// Whispers

test("Pressing space after /w triggers suggestions", function (assert) {
  assert.expect(1);

  this.set('textareaValue', '/w');

  this.renderTemplate();

  dispatchKeyEvent('keydown', {
    target: this.$('textarea.mousetrap'),
    keyCode: 32
  });

  return wait().then(() => {
    assert.elementText(this.$('.suggestion'), 'No matches.');
  });
});

test('pressing up arrow shows previous whisper target if exists', function (assert) {
  assert.expect(1);

  this.set('suggestions', largeSuggestions);

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 38 });
  return wait().then(() => {
    assert.equal(target.val(), '/w btest4 ');
  });
});

test('pressing down arrow shows next whisper target if it exists', function (assert) {
  assert.expect(1);

  this.set('suggestions', largeSuggestions);

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 38 });
  return wait().then(() => {
    dispatchKeyEvent('keydown', { target, keyCode: 38 });
    return wait();
  }).then(() => {
    dispatchKeyEvent('keydown', { target, keyCode: 38 });
    return wait();
  }).then(() => {
    dispatchKeyEvent('keydown', { target, keyCode: 40 });
    return wait();
  }).then(() => {
    assert.equal(target.val(), '/w ctest9 ');
  });
});

test('pressing up arrow past list of whisper targets creates empty field', function (assert) {
  assert.expect(2);

  this.set('suggestions', largeSuggestions);

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 38 });
  return wait().then(() => {
    dispatchKeyEvent('keydown', { target, keyCode: 38 });
    return wait();
  }).then(() => {
    dispatchKeyEvent('keydown', { target, keyCode: 38 });
    return wait();
  }).then(() => {
    assert.equal(target.val(), '/w iamauser ');
    dispatchKeyEvent('keydown', { target, keyCode: 38 });
    return wait();
  }).then(() => {
    assert.equal(target.val(), '');
  });
});

test('pressing down arrow past list of whisper targets creates empty field', function (assert) {
  assert.expect(2);

  this.set('suggestions', largeSuggestions);

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 38 });
  return wait().then(() => {
    assert.equal(target.val(), '/w btest4 ');
    dispatchKeyEvent('keydown', { target, keyCode: 40 });
    return wait();
  }).then(() => {
    assert.equal(target.val(), '');
  });
});

test('pressing up arrow with no whisper targets does nothing', function (assert) {
  assert.expect(1);

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 38 });
  return wait().then(() => {
    assert.equal(target.val(), '');
  });
});

test('pressing down arrow with no whisper targets does nothing', function (assert) {
  assert.expect(1);

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 40 });
  return wait().then(() => {
    assert.equal(target.val(), '');
  });
});

test('whisper cycle does not work with preexisting message', function (assert) {
  assert.expect(1);

  this.set('suggestions', largeSuggestions);
  this.set('textareaValue', 'hey whats up');

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 38 });
  return wait().then(() => {
    assert.equal(target.val(), 'hey whats up');
  });
});

test('whisper cycle only cycles through unique usernames', function (assert) {
  assert.expect(1);

  this.set('suggestions', () => {
    return [{
      id: 'iamauser',
      displayName: 'iamauser',
      whispered: true,
      timestamp: 1
    }, {
      id: 'iamauser',
      displayName: 'iamauser',
      whispered: true,
      timestamp: 2
    }];
  });

  this.renderTemplate();

  let target = this.$('textarea.mousetrap');
  dispatchKeyEvent('keydown', { target, keyCode: 38 });
  return wait().then(() => {
    dispatchKeyEvent('keydown', { target, keyCode: 38 });
    return wait();
  }).then(() => {
    assert.equal(target.val(), '');
  });
});
