import { exec } from 'child_process';
import psTree from 'ps-tree';
import nconf from 'nconf';
import net from 'net';
import { post } from 'superagent';
import { sync as glob } from 'glob';
import Mocha from 'mocha'; // eslint-disable-line import/no-extraneous-dependencies
import { resolve } from 'path';

/*
 * Get access to configruable values
 */
nconf.argv().env().file({ file: 'config.json' });
export const conf = nconf;

/*
 * Kill a child process and any sub-children that process may have spawned.
 * This is necessary to ensure that Gulp will terminate when it has completed
 * its tasks.
 */
export function kill (proc) {
  const killProcess = pid => {
    psTree(pid, (_, pids) => {
      if (pids.length) {
        pids.forEach(kill); return;
      }
      try {
        exec(/^win/.test(process.platform)
          ? `taskkill /PID ${pid} /T /F`
          : `kill -9 ${pid}`);
      } catch (e) {
        console.log(e); // eslint-disable-line no-console
      }
    });
  };

  killProcess(proc.PID || proc.pid);
}

/*
 * Return a promise that will execute when Node is able to connect on a
 * specific port. For example, this can be used to halt tasks until Selenium
 * has fully spun up. Optionally provide a maximum number of seconds to wait
 * before failing.
 */
export function awaitPort (port, max = 60) {
  return new Promise((rej, res) => {
    let socket;
    let interval;

    const timeout = setTimeout(() => {
      clearInterval(interval);
      rej(`Timed out after ${max} seconds`);
    }, max * 1000);

    interval = setInterval(() => {
      socket = net.connect({ port }, () => {
        clearInterval(interval);
        clearTimeout(timeout);
        socket.destroy();
        res();
      }).on('error', () => {
        socket.destroy();
      });
    }, 1000);
  });
}

/*
 * Pipe the child's stdin and stderr to the parent process.
 */
export function pipe (child) {
  child.stdout.on('data', data => {
    process.stdout.write(data);
  });
  child.stderr.on('data', data => {
    process.stderr.write(data);
  });
}

/*
 * Post request to notify configured slack channel
 */
export function postToSlack (msg, config = {}) {
  const slackUrl = nconf.get('SLACK_URL');

  if (!slackUrl) {
    console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
    console.log(msg); // eslint-disable-line no-console

    return;
  }

  post(slackUrl)
    .send({
      channel: `#${config.channel || '#general'}`,
      username: config.username || 'gulp task',
      text: msg,
      icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase
    })
    .end(err => {
      if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console
    });
}

export function runMochaTests (files, server, cb) {
  require('../test/helpers/globals.helper'); // eslint-disable-line global-require

  const mocha = new Mocha({ reporter: 'spec' });
  const tests = glob(files);

  tests.forEach(test => {
    delete require.cache[resolve(test)];
    mocha.addFile(test);
  });

  mocha.run(numberOfFailures => {
    if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env
      if (server) kill(server);
      process.exit(numberOfFailures);
    }
    cb();
  });
}
