/* globals Twitch */

import Ember from 'ember';

const { inject, RSVP, Service } = Ember;
const regexRegex = /[\|\\\^\$\*\+\?\:\#]/;
const htmlEntityLookup = {
  '<' : '&lt;',
  '>' : '&gt;'
};

export default Service.extend({
  api: inject.service(),
  session: inject.service(),

  init() {
    this._super(...arguments);
    this.populate().then((emotes) => {
      if (this.isDestroyed) { return ; }
      this.set("_emotes", emotes);
    });
  },

  populate() {
    if (this.get('_populationPromise')) {
      return this.get('_populationPromise');
    }

    let populationPromise;

    if (this._isLoggedIn()) {
      populationPromise = this.get('session').getCurrentUser().then((user) => {
        if (this.isDestroyed) { return; }
        return this.get('api').request('get', `users/${user.login}/emotes`).then(
          response => response,
          () => { return {}; }
        );
      }, error => {
        // 401 is an expected error status that means the user is
        // unauthenticated
        if (error && error.status === 401) {
          return {};
        }
        // Otherwise, rethrow
        throw error;
      });

    } else {
      populationPromise = RSVP.resolve({});
    }

    this.set('_populationPromise', populationPromise);

    return populationPromise;
  },

  _isLoggedIn() {
    return Twitch.user.isLoggedIn();
  },

    // Formats emotes to be consumed by feeds' components
  _buildEmoteSet(emoteSets) {
    let emotes = [];

    let processEmote = (setEmote) => {
      let code = setEmote.code;
      let isRegex = regexRegex.test(setEmote.code);
      if (isRegex) {
        code = new RegExp(code);
      }
      emotes.push({
        id: setEmote.id,
        code: code,
        isRegex
      });
    };

    let emoteSetKeys = Object.keys(emoteSets);
    for (let i = 0; i < emoteSetKeys.length; i++) {
      let key = emoteSetKeys[i];
      let emoteSet = emoteSets[key];
      emoteSet.forEach(processEmote);
    }
    return emotes;
  },

  // Takes a string and an array of emotes
  // returns null || id
  _testWord (word, emoteSet) {
    word = this._cleanEmoteName(word);
    for (let i = 0; i < emoteSet.length; i++) {
      let emote = emoteSet[i];
      if (emote.isRegex && word.search(emote.code) === 0) {
        return emote.id;
      }
      if (!emote.isRegex && emote.code === word) {
        return emote.id;
      }
    }
    return null;
  },

  _parseEmotes (string, emotes) {
    let words = string.split(' ');
    let matches = [];
    let startIdx = 0;
    words.forEach((token) => {
      let match = null;
      if (token.length) {
        match = this._testWord(token, emotes);
      }
      if (match !== null) {
        matches.push({
          id: match,
          start: startIdx,
          end: (startIdx + token.length) - 1
        });
      }
      startIdx += token.length + 1;
    });
    return matches;
  },

  // Takes an emote name and replaces select character entities with it reserved character.
  // This is required as certain emotes regex representations are not picked up by _testWord ("<3" for example)
  _cleanEmoteName(emoteName) {
    let entityKeys = Object.keys(htmlEntityLookup);
    for (let i = 0; i < entityKeys.length; i++) {
      let key = entityKeys[i];
      if (emoteName.indexOf(key) > -1) {
        return emoteName.replace(key, htmlEntityLookup[key]);
      }
    }

    return emoteName;
  },

  // Takes a string
  // Returns a promise that resolves to the matched emotes that you are
  // authorized to post as an array of token descriptors of type:
  // {
  //   id: int ID of emote detected,
  //   start: string character index starting emote
  //   end: string character index ending emote
  // }
  parseEmotes (string) {
    return this.populate().then((emotes) => {
      return this._buildAndParseEmotes(string, emotes);
    });
  },

  // Useful if you want a synchronous version
  tryParseEmotes (string) {
    let emotes = this.get("_emotes");
    if (!emotes) {
      return [];
    }

    return this._buildAndParseEmotes(string, emotes);
  },

  _buildAndParseEmotes(string, emotes) {
    let emoteSet = this._buildEmoteSet(emotes.emoticon_sets);
    return this._parseEmotes(string, emoteSet);
  }
});
