/*
  2/6/2017 - Brian Chan

  This is a text field component that will populate a dropdown balloon in real
  time as the user enters a game. Users are then able to click on one of the
  game selections and a game object containing the game `id` and `name` will be
  sent via `didResolveGame`. Consumers can also listen for `didBeginSearch` to
  learn when the game-select component starts a new search and is unsure
  whether the current game entered by the user exists on the server.

  NOTE: If user selects "Programming" or "Game Development" as their game then
  the game should be changed to "Creative" with the hashtag "#programming" or
  "#gamedev" appended to the title respectively. This behavior will need to be
  done by the consumer of {{game-select}}. Please see example from
  components/dashboards/live/edit-broadcast/component.js
*/

import Component from 'ember-component';
import injectService from 'ember-service/inject';
import ClickOutsideMixin from 'web-client/mixins/click-outside';
import config from 'web-client/config/environment';
import run from 'ember-runloop';

const WAIT_FOR_TYPING = config.delay.gameSelect.waitForUserTyping;

export default Component.extend(ClickOutsideMixin, {
  classNames: ['balloon-wrapper'],

  api: injectService(),

  /* passed in properties */
  inputClass: '',
  gameName: null,
  testSelector: null,
  didBeginSearch() {},
  didResolveGame() {},

  /* internal properties */
  gameList: null,
  isShowingGameList: false,
  queryTerm: '',
  didStopTypingTask: null,

  // Keep track of inFlight requests so we don't resolve while waiting on a
  // request
  inFlightRequests: 0,

  // Keep track of when the user is typing so that we don't resolve while they
  // are typing
  isUserTyping: false,

  // If user clicks outside the <input> or balloon
  clickedOutside() {
    this.set('isShowingGameList', false);
  },

  didReceiveAttrs() {
    if (this.queryTerm !== this.get('gameName')) {
      this.queryTerm = this.get('gameName');
      run.cancel(this.didStopTypingTask);
      this.set('isShowingGameList', false);
    }
  },

  actions: {
    onInput(value) {
      this.isUserTyping = true;
      this.didStopTypingTask = run.debounce(this, 'userDidStopTyping', WAIT_FOR_TYPING);
      this.queryTerm = value;

      if (value === '') {
        this.game = ({ name: '', id: null });
        this.userDidStopTyping();
        this.set('isShowingGameList', false);
      } else {
        this.didBeginSearch({ name: value, id: null });
        this.query();
      }
    },

    onGameSelect(game) {
      this.get('didResolveGame')({ name: game.name, id: game._id });
      this.set('isShowingGameList', false);
    },

    onInputClick() {
      if (this.get('gameList.length') && this.queryTerm) {
        this.set('isShowingGameList', true);
      }
    }
  },

  userDidStopTyping() {
    run.cancel(this.didStopTypingTask);
    this.isUserTyping = false;
    this.resolveGame();
  },

  resolveGame() {
    if (this.isUserTyping || this.inFlightRequests > 0) {
      return;
    }

    this.get('didResolveGame')({
      name: (this.game && this.game.name) || this.queryTerm,
      id: (this.game && this.game._id) || null
    });
  },

  query() {
    let originalQueryTerm = this.queryTerm;
    this.inFlightRequests++;

    this.get('api').request('get', 'search/games', { query: this.queryTerm, type: 'suggest' })
      .then(response => {
        if (this.isDestroyed || (this.queryTerm !== originalQueryTerm)) {
          return;
        }

        this.game = response.games.find(g => {
          return areCaseInsensitiveEqual(g.name, this.queryTerm);
        });

        this.set('gameList', response.games);
        this.set('isShowingGameList', true);
      }).finally(() => {
        this.inFlightRequests--;
        this.resolveGame();
      });
  },

  willDestroy() {
    this._super(...arguments);
    run.cancel(this.didStopTypingTask);
  }
});

const areCaseInsensitiveEqual = (s1, s2) => s1.toLowerCase() === s2.toLowerCase();
