const electron = require('electron');
const bunyan = require('bunyan');
const _ = require('lodash');


const ipc = electron.ipcRenderer;
const PHONE_REGEX = /\+\d{7,12}(\d{3})/g;
const GROUP_REGEX = /(group\()([^)]+)(\))/g;

// Default Bunyan levels: https://github.com/trentm/node-bunyan#levels
// To make it easier to visually scan logs, we make all levels the same length
const BLANK_LEVEL = '     ';
const LEVELS = {
  60: 'fatal',
  50: 'error',
  40: 'warn ',
  30: 'info ',
  20: 'debug',
  10: 'trace',
};


// Backwards-compatible logging, simple strings and no level (defaulted to INFO)

function redactPhone(text) {
  return text.replace(PHONE_REGEX, "+[REDACTED]$1");
}

function redactGroup(text) {
  return text.replace(GROUP_REGEX, function(match, before, id, after) {
    return before + '[REDACTED]' + id.slice(-3) + after;
  });
}

function now() {
  const date = new Date();
  return date.toJSON();
}

function log() {
  const args = Array.prototype.slice.call(arguments, 0);

  const consoleArgs = ['INFO ', now()].concat(args);
  console._log.apply(console, consoleArgs);

  // To avoid [Object object] in our log since console.log handles non-strings smoothly
  const str = args.map(function(item) {
    if (typeof item !== 'string') {
      try {
        return JSON.stringify(item);
      }
      catch (e) {
        return item;
      }
    }

    return item;
  });
  const toSend = redactGroup(redactPhone(str.join(' ')));
  ipc.send('log-info', toSend);
}

if (window.console) {
  console._log = console.log;
  console.log = log;
}


// The mechanics of preparing a log for publish

function getHeader() {
  return window.navigator.userAgent + ' node/' + window.config.node_version;
}

function getLevel(level) {
  var text = LEVELS[level];
  if (!text) {
    return BLANK_LEVEL;
  }

  return text.toUpperCase();
}

function formatLine(entry) {
  return getLevel(entry.level) + ' ' + entry.time + ' ' + entry.msg;
}

function format(entries) {
  return redactGroup(redactPhone(entries.map(formatLine).join('\n')));
}

function fetch() {
  return new Promise(function(resolve) {
    ipc.send('fetch-log');

    ipc.on('fetched-log', function(event, text) {
      var result = getHeader() + '\n' + format(text);
      resolve(result);
    });
  });
}

function publish(log) {
  log = log || fetch();

  return new Promise(function(resolve) {
    const payload = textsecure.utils.jsonThing({
      files: {
        'debugLog.txt': {
          content: log
        }
      }
    });

    $.post('https://api.github.com/gists', payload)
      .then(function(response) {
        console._log('Posted debug log to ', response.html_url);
        resolve(response.html_url);
      })
      .fail(resolve);
  });
}


// A modern logging interface for the browser

// We create our own stream because we don't want to output JSON to the devtools console.
//   Anyway, the default process.stdout stream goes to the command-line, not the devtools.
const logger = bunyan.createLogger({
  name: 'log',
  streams: [{
    level: 'debug',
    stream: {
      write: function(entry) {
        console._log(formatLine(JSON.parse(entry)));
      }
    }
  }]
});

// The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api
function logAtLevel() {
  const level = arguments[0];
  const args = Array.prototype.slice.call(arguments, 1);

  const ipcArgs = ['log-' + level].concat(args);
  ipc.send.apply(ipc, ipcArgs);

  logger[level].apply(logger, args);
}

window.log = {
  fatal: _.partial(logAtLevel, 'fatal'),
  error: _.partial(logAtLevel, 'error'),
  warn: _.partial(logAtLevel, 'warn'),
  info: _.partial(logAtLevel, 'info'),
  debug: _.partial(logAtLevel, 'debug'),
  trace: _.partial(logAtLevel, 'trace'),
  fetch,
  publish,
};

window.onerror = function(message, script, line, col, error) {
  window.log.error(error.stack);
};