/* eslint-env node */

/* eslint strict: ['error', 'never'] */

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

const debuglogs = require('./modules/debuglogs');
const Privacy = require('./modules/privacy');

const ipc = electron.ipcRenderer;

// 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 now() {
  const date = new Date();
  return date.toJSON();
}

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

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

    return item;
  });

  const logText = Privacy.redactAll(str.join(' '));
  ipc.send('log-info', logText);
}

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

// The mechanics of preparing a log for publish

function getHeader() {
  let header = window.navigator.userAgent;

  header += ` node/${window.config.node_version}`;
  header += ` env/${window.config.environment}`;

  return header;
}

function getLevel(level) {
  const 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 Privacy.redactAll(entries.map(formatLine).join('\n'));
}

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

    ipc.on('fetched-log', (event, text) => {
      const result = `${getHeader()}\n${format(text)}`;
      resolve(result);
    });
  });
}

const publish = debuglogs.upload;

// 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(entry) {
          console._log(formatLine(JSON.parse(entry)));
        },
      },
    },
  ],
});

// The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api
function logAtLevel(level, ...args) {
  const ipcArgs = [`log-${level}`].concat(args);
  ipc.send(...ipcArgs);

  logger[level](...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 = (message, script, line, col, error) => {
  const errorInfo = error && error.stack ? error.stack : JSON.stringify(error);
  window.log.error(`Top-level unhandled error: ${errorInfo}`);
};

window.addEventListener('unhandledrejection', rejectionEvent => {
  window.log.error(
    `Top-level unhandled promise rejection: ${rejectionEvent.reason}`
  );
});
