#!/usr/bin/env node

var program = require('commander');
var spawn = require('child_process').spawn;
var fs = require('fs');
var ListSuites = require('./utils/ListSuites');

let suite;

program
  .description('Run the tests for the current suite or the provided one.')
  .arguments('[suite]')
  .option('--headless', 'Run in headless mode.')
  .option('--forbid-only', 'Forbid only and pending filters.')
  .action((suiteArg) => suite = suiteArg)
  .parse(process.argv);

async function runTests(suite, withEnv) {
  return new Promise((resolve, reject) => {
    let mochaArgs = ['--exit', '--colors', '--require', 'ts-node/register', 'test/suites/' + suite + '/test.ts']
    if (program.forbidOnly) {
      mochaArgs = ['--forbid-only', '--forbid-pending'].concat(mochaArgs);
    }
  
    const mochaCommand = './node_modules/.bin/mocha ' + mochaArgs.join(' ');
    let testProcess;
    if (!withEnv) {
      testProcess = spawn(mochaCommand, {
        shell: true,
        env: {
          ...process.env,
          TS_NODE_PROJECT: 'test/tsconfig.json',
          HEADLESS: (program.headless) ? 'y' : 'n'
        }
      });
    } else {
      testProcess = spawn('./node_modules/.bin/ts-node',
        ['-P', 'test/tsconfig.json', '--', './scripts/run-environment.ts', suite, mochaCommand], {
        env: {
          ...process.env,
          TS_NODE_PROJECT: 'test/tsconfig.json',
          HEADLESS: (program.headless) ? 'y' : 'n'
        }
      });
    }
  
    testProcess.stdout.pipe(process.stdout);
    testProcess.stderr.pipe(process.stderr);
  
    testProcess.on('exit', function(statusCode) {
      if (statusCode == 0) resolve();
      reject(new Error('Tests exited with status ' + statusCode));
    });
  });
}

async function runAllTests() {
  const suites = ListSuites();
  let failure = false;
  for(var s in suites) {
    try {
      console.log('Running suite %s', suites[s]);
      await runTests(suites[s], true);
    } catch (e) {
      console.error(e);
      failure = true;
    }
  }
  if (failure) {
    throw new Error('Some tests failed.');
  }
}

const ENVIRONMENT_FILENAME = '.suite'; // This file is created by the start command.
const suiteFileExists = fs.existsSync(ENVIRONMENT_FILENAME);

if (suite) {
  if (suiteFileExists && suite != fs.readFileSync(ENVIRONMENT_FILENAME)) {
    console.error('You cannot test a suite while another one is running. If you want to test the current suite, ' +
                  'do not provide the suite argument. Otherwise, stop the current suite and run the command again.');
    process.exit(1);
  }
  console.log('Suite %s provided. Running test related to this suite against built version of Authelia.', suite);
  runTests(suite, !suiteFileExists)
    .catch((err) => {
      console.error(err);
      process.exit(1);
    });
}
else if (suiteFileExists) {
  suite = fs.readFileSync(ENVIRONMENT_FILENAME);
  console.log('Suite %s detected from dev env. Running test related to this suite.', suite);
  runTests(suite, false)
    .catch((err) => {
      console.error(err);
      process.exit(1);
    });
}
else {
  console.log('No suite provided therefore all suites will be tested.');
  runAllTests(suite)
    .catch((err) => {
      console.error(err);
      process.exit(1);
    });
}

// Sometime SIGINT is received twice, we make sure with this variable that we cleanup
// only once.
var alreadyCleaningUp = false;

// Properly cleanup server and client if ctrl-c is hit
process.on('SIGINT', function() {
  if (alreadyCleaningUp) return;
  alreadyCleaningUp = true;
  console.log('Cleanup procedure is running...');
});