const async = require('async'),
      CrashReporter = require('./services/CrashReporter'),
      Logger = require('./services/Logger'),
      { isProcessAlive } = require('./helpers/process'),
      PluginInstallationService = require('./services/PluginInstallationService'),
      SIGPIPE = 'SIGPIPE',
      NOOP = () => {
        // noop
      },
      parentPid = Number(process.env.POSTMAN_PARENT_PID); // eslint-disable-line no-process-env

// The SIGPIPE errors are purposefully silenced as they are invalid for now.
// This happened when linux tries to auto-update for the first time with plugin host.
process.on(SIGPIPE, NOOP);

/**
 * @method sendToParent
 * @description It is used to send the information to parent. It adds timestamp to the data
 * @params {Object} data
 */
function sendToParent (data) {
  process.send(Object.assign({}, data, { timestamp: Date.now() }));
}

/**
 * @method subscribeToParent
 * @description It subscribes the parent channel for any message
 * @todo Communication interface needs to be implemented here
 */
function subscribeToParent () {
  process.on('message', (msg) => {
    pm.logger.info('Message from parent:', msg);
  });
}

/**
 * @method attachExitHandler
 * @description It attaches the exit listener on the process that information will be passed on the parent
 */
function attachExitHandler () {
  process.on('exit', (code) => {
    sendToParent({ type: 'exit', code });
  });
}

/**
 * @method dieIfOrphan
 * @description It checks for the parent process availibility every 5sec and dies if it is not there
 * It uses process.env.POSTMAN_PARENT_PID
 */
function dieIfOrphan () {
  setInterval(() => {
    !isProcessAlive(parentPid) && process.exit();
  }, 5000);
}

/**
 * @method attachListenersAndHandlers
 * @description It is used to attach the listeners and handlers for the process
 * @param {Function} cb
 */
function attachListenersAndHandlers (cb) {
  attachExitHandler();
  subscribeToParent();
  dieIfOrphan();
  process.nextTick(cb);
}

/**
 * Extracts all the builtin plugins the first time. After that, whenever a builtin plugin gets
 * updated this will extract that also
 *
 * @param {Function} cb
 */
function installOrUpdatePlugins (cb) {
  PluginInstallationService.installOrUpdatePlugins()
    .then((results) => {
      pm.logger.info('PluginsHost~installOrUpdatePlugins: Synced all plugins', results);
    })
    .catch((err) => {
      pm.logger.warn('PluginsHost~installOrUpdatePlugins: Error while syncing plugins', err);
    });

  // Not blocking the boot sequence on the plugin syncing
  process.nextTick(cb);
}

// Initialize pm object
global.pm = global.pm || {};

// Boot sequence
async.series([
  CrashReporter.init,
  Logger.init,
  attachListenersAndHandlers,
  installOrUpdatePlugins
], (err, results) => {
  if (err) {
    pm.logger.error('PluginsHost: Error in booting plugin host process', err, results);
    return;
  }
  pm.logger.info('PluginsHost: Booted');
});

