/* eslint-disable */
'use strict';

const spawn = require('child_process').spawn;
const TapParser = require('tap-parser');
const request = require('request-json');

const client = request.createClient(process.env.APPVEYOR_API_URL);

function Batcher(action, batchSize, batchIntervalInMs, cb) {
  const queue = this.queue = [];
  let complete = false;
  let busy = false;
  const flush = () => {
    if (busy) return;
    const items = queue.splice(0, batchSize);
    if (!items.length) {
      return complete && cb();
    }
    busy = true;
    action(items, err => {
      if (err) {
        clearInterval(schedule);
        return cb(err);
      }
      busy = false;
      if (complete) {
        clearInterval(schedule);
        flush();
      }
    });
  };

  this.end = () => {
    complete = true;
    if (!busy) {
      clearInterval(schedule);
      flush();
    }
  };
  const schedule = setInterval(flush, batchIntervalInMs);
}

Batcher.prototype.push = function (item) {
  this.queue.push(item);
};

function report(cb) {
  const parser = new TapParser();
  const batcher = new Batcher(
    (events, cb) => client.post('api/tests/batch', events, cb), 100, 1000, cb);
  parser.on('complete', () => batcher.end());
  parser.on('assert', event => batcher.push(mapAssert(event)));
  parser.on('line', line => process.stdout.write(line));
  return parser;
}

function mapAssert(event) {
  return {
    testName: uniquify(event.name),
    outcome: event.ok ? 'Passed'
      : event.skip ? 'Skipped'
      : event.todo ? 'Ignored'
      : 'Failed',
    ErrorMessage: event.diag ? event.diag.message : void 0,
    ErrorStackTrace: event.diag ? event.diag.at : void 0,
  };
}

function test(cmd, cb) {
  const ytc = spawn('yarn', [cmd, '--', '--tap'],
    { shell: true, stdio: ['pipe', 'pipe', 'inherit'] });
  ytc.stdout.pipe(report(handleErr(cb)));
  ytc.on('close', code => code && (process.exitCode = code));
}

const handleErr = fn => err => {
  if (err) {
    console.error(err);
    process.exitCode = 1;
  } else {
    fn();
  }
};

const nameCounts = new Map();
function uniquify(name) {
  if (nameCounts.has(name)) {
    const count = nameCounts.get(name) + 1;
    nameCounts.set(name, count);
    return `${name} (${count})`;
  } else {
    nameCounts.set(name, 1);
    return name;
  }
}

const justCoverage = () => test('test:coverage', () => {});
if (process.env.include_spawn) {
  test('test:spawn', justCoverage);
} else {
  justCoverage();
}
