/* globals URI, punycode */
import Ember from 'ember';
import Component from 'ember-component';
import { EMOTICONS_V1_URL } from 'web-client/utilities/urls/static-cdn';
import { assign } from 'ember-platform';
import { htmlSafe } from 'ember-string';

const urlRegex = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&//=]*)/g;
const { Handlebars } = Ember;

export default Component.extend({
  body: null,
  emotes: null,
  truncateAfter: null,
  numPreviewLines: null,
  bodyTruncated: false,
  tagName: 'div',
  classNameBindings: [':activity-body', 'bodyTruncated:activity-body--truncated'],

  init() {
    this._super(...arguments);
    this._processBody();
  },

  _makeLink(link) {
    // TODO: Need to add link parsing here to make sure a protocol is specified.
    let uri = new URI(link.url);
    if (uri.protocol() === "") {
      uri.protocol('http');
    }
    return `<a class="js-feedlink" href="${uri.toString()}" target="_blank" rel="noopener noreferrer">${link.text}</a>`;
  },

  _makeEmote(emote) {
    let url = `${EMOTICONS_V1_URL}/${emote.id}/1.0`;
    let srcSet = `${EMOTICONS_V1_URL}/${emote.id}/2.0 2x`;
    let altText = emote.text;

    return `<img class="emoticon js-emoticon tooltip" src="${url}" alt="${altText}" title="${altText}" srcset="${srcSet}" />`;
  },

  _textToToken(text, offset) {
    return {text, start: offset, end: offset + text.length - 1};
  },

  _truncateTokenText(token, truncateAfter) {
    let after = token.text.length + token.start;
    if (truncateAfter) {
      if (truncateAfter <= token.start) {
        this.set('bodyTruncated', true);
        return '';
      }
      if (truncateAfter < after) {
        this.set('bodyTruncated', true);
        return token.text.slice(0, truncateAfter - after);
      }
    }
    return token.text;
  },

  _restHandler(token, truncateAfter) {
    let rest = this._truncateTokenText(token, truncateAfter);
    return rest ? [Handlebars.Utils.escapeExpression(rest)] : [];
  },

  /* Takes a string body
   Returns an array of token descriptors:
   {
    link: "www.google.com",
    start: 0,
    end: 14
   }
  */
  _detectLinks(body) {
    let linkMatches = body.match(urlRegex);
    let links = [];
    if (linkMatches) {
      let searchIdx = 0;
      linkMatches.forEach((match) => {
        let startIdx = body.indexOf(match, searchIdx);
        let endIdx = startIdx + match.length - 1;
        searchIdx = endIdx;
        links.push({
          link: match,
          start: startIdx,
          end: endIdx
        });
      });
    }
    return links;
  },

  /*
  Method that grabs a string, an optional emotes array, and an option truncation number.

  Will replace emotes, apply link detection, and ensure output is escaped.
  */
  _processBody() {
    let body = this.get('body');
    let emotes = this.get('emotes');
    let truncateAfter = this.get('truncateAfter');
    let numPreviewLines = this.get('numPreviewLines');

    if (!body) { return body; }

    body = body.toString();
    emotes = emotes || [];

    // Sort tokens by token.start
    let tokens = [...emotes, ...this._detectLinks(body)]
      .sort((tokenA, tokenB) => tokenA.start - tokenB.start);

    let parsedStrings = [];

    let currentIdx = 0;
    let rest = punycode.ucs2.decode(body);
    tokens.forEach((token) => {
      let head = rest.slice(0, token.start - currentIdx);
      let start = token.start - currentIdx;
      let end = token.end - currentIdx;

      let tokenText = rest.slice(start, end + 1);
      if (typeof truncateAfter === 'number' && truncateAfter <= token.start) {
        tokenText = '';
      } else if ('link' in token) {
        tokenText = this._truncateTokenText(assign({text: tokenText}, token), truncateAfter);
      }
      if (tokenText) {
        if ('link' in token) {
          tokenText = this._makeLink({text: punycode.ucs2.encode(tokenText), url: token.link});
        } else {
          tokenText = this._makeEmote({text: punycode.ucs2.encode(tokenText), id: token.id});
        }
      }

      parsedStrings.push(...this._restHandler(this._textToToken(punycode.ucs2.encode(head), currentIdx), truncateAfter), tokenText);

      rest = rest.slice(end + 1);
      currentIdx += end + 1;
    });

    parsedStrings.push(...this._restHandler(this._textToToken(punycode.ucs2.encode(rest), currentIdx), truncateAfter));
    let processedString = parsedStrings.join('');
    processedString = processedString.split('\n').map((str) => `<p>${str}</p>`);
    if (numPreviewLines && numPreviewLines < processedString.length) {
      processedString = processedString.slice(0, numPreviewLines);
      this.set('bodyTruncated', true);
    }
    processedString = processedString.join('\n');

    this.set('processedBody', htmlSafe(processedString));
  },

  actions: {
    showFullBody() {
      this.set('truncateAfter', null);
      this.set('numPreviewLines', null);
      this._processBody();
      this.set('bodyTruncated', false);
    }
  }
});
