
import moment from 'moment-timezone';
import { ansiToHtml } from 'anser';
import { API_ORIGIN } from '../constants.js';

// https://github.com/janl/mustache.js/blob/master/mustache.js#L60
const entityMap = {
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  '\'': '&#39;',
  '/': '&#x2F;',
  '`': '&#x60;',
  '=': '&#x3D;'
};

function escapeHtml(string) {
  return String(string).replace(/[&<>"'`=/]/g, function fromEntityMap (s) {
    return entityMap[s];
  });
}

function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint16Array(buf));
}

export function create(type, id, options = {}) {
  const accessToken = localStorage.token;
  const INITIAL_STREAM_LINES = 100;

  let eventSource = null;

  let streamApi = '';
  let downloadApi = '';

  if (type === 'platform') {
    streamApi = '/api/v1/system/logstream/box';
    downloadApi = '/api/v1/system/logs/box';
  } else if (type === 'crash') {
    streamApi = `/api/v1/system/logstream/crash-${id}`;
    downloadApi = `/api/v1/system/logs/crash-${id}`;
  } else if (type === 'app') {
    streamApi = `/api/v1/apps/${id}/logstream`;
    downloadApi = `/api/v1/apps/${id}/logs`;
  } else if (type === 'service') {
    streamApi = `/api/v1/services/${id}/logstream`;
    downloadApi = `/api/v1/services/${id}/logs`;
  } else if (type === 'task' && options.appId) {
    streamApi = `/api/v1/apps/${options.appId}/tasks/${id}/logstream`;
    downloadApi = `/api/v1/apps/${options.appId}/tasks/${id}/logs`;
  } else if (type === 'task') {
    streamApi = `/api/v1/tasks/${id}/logstream`;
    downloadApi = `/api/v1/tasks/${id}/logs`;
  } else {
    console.error('unsupported logs type', type);
  }

  return {
    name: 'LogsModel',
    stream(lineHandler, errorHandler) {
      eventSource = new EventSource(`${API_ORIGIN}${streamApi}?lines=${INITIAL_STREAM_LINES}&access_token=${accessToken}`);
      eventSource._lastMessage = null;
      eventSource.onerror = function ( /* uselessError */) {
        if (eventSource.readyState === EventSource.CLOSED) {
          // eventSource does not give us the HTTP error code. We have to resort to message count check and guess the reason
          const msg = eventSource._lastMessage === null ? `Logs unavailable. Maybe the logs were logrotated.` : `Connection closed.`;
          const e = new Error(msg);
          e.time = moment().format('MMM DD HH:mm:ss');
          e.html = ansiToHtml(e.message);
          errorHandler(e);
        }
      };
      // eventSource.onopen = function () { console.log('stream is open'); };
      eventSource.onmessage = function (message) {
        var data;

        try {
            data = JSON.parse(message.data);
        } catch (e) {
            return console.error(e);
        }

        const time = data.realtimeTimestamp ? moment(data.realtimeTimestamp/1000).format('MMM DD HH:mm:ss') : '';
        const html = ansiToHtml(escapeHtml(typeof data.message === 'string' ? data.message : ab2str(data.message)));

        eventSource._lastMessage = { time, html };
        lineHandler(time, html);
      };
    },
    getDownloadUrl() {
      return `${API_ORIGIN}${downloadApi}?access_token=${accessToken}&format=short&lines=-1`;
    }
  };
}

export default {
  create
};
