#!/usr/bin/env node

var program = require('commander');
var spawn = require('child_process').spawn;
var chokidar = require('chokidar');
var fs = require('fs');

program
  .option('-s, --suite <suite>', 'The suite to run Authelia for. This suite represents a configuration for Authelia and a set of tests for that configuration.')
  .parse(process.argv)

if (!program.suite) {
  throw new Error('Please provide a suite.');
}

const ENVIRONMENT_FILENAME = '.suite';


var tsWatcher = chokidar.watch(['server', 'shared/**/*.ts', 'node_modules'], {
  persistent: true,
  ignoreInitial: true,
});

// Properly cleanup server and client if ctrl-c is hit
process.on('SIGINT', function() {
  killServer(() => {});
  killClient(() => {});
  fs.unlinkSync(ENVIRONMENT_FILENAME);
  process.exit();
});


let serverProcess;
function reloadServer() {
  killServer(() => {
    startServer();
  });
}

function startServer() {
  serverProcess = spawn('./scripts/run-dev-server.sh', [`test/suites/${program.suite}/config.yml`], {detached: true});
  serverProcess.stdout.pipe(process.stdout);
  serverProcess.stderr.pipe(process.stderr);
}

let clientProcess;
function startClient() {
  clientProcess = spawn('npm', ['run', 'start'], {
    detached: true,
    cwd: './client',
    env: {
      ...process.env,
      'BROWSER': 'none'
    }
  });
  clientProcess.stdout.pipe(process.stdout);
  clientProcess.stderr.pipe(process.stderr);
}

function killServer(onExit) {
  if (serverProcess) {
    serverProcess.on('exit', () => {
      serverProcess = undefined;
      onExit();
    });
    try {
      process.kill(-serverProcess.pid);
    } catch (e) {
      console.error(e);
      onExit();
    }
  }
}

function killClient(onExit) {
  if (clientProcess) {
    clientProcess.on('exit', () => {
      clientProcess = undefined;
      onExit();
    });
    try {
      process.kill(-clientProcess.pid);
    } catch (e) {
      console.error(e);
      onExit();
    }
  }
}

function generateConfigurationSchema() {
  exec('./node_modules/.bin/typescript-json-schema -o server/src/lib/configuration/Configuration.schema.json --strictNullChecks --required server/tsconfig.json Configuration');
}

function reload(path) {
  console.log(`File ${path} has been changed, reloading...`);
  if (path.startsWith('server/src/lib/configuration/schema')) {
    console.log('Schema needs to be regenerated.');
    generateConfigurationSchema();
  }
  reloadServer();
}

function exec(command, args) {
  return new Promise((resolve, reject) => {
    const cmd = spawn(command, args);

    cmd.stdout.pipe(process.stdout);
    cmd.stderr.pipe(process.stderr);
    cmd.on('close', (code) => {
      if (code == 0) {
        resolve();
        return;
      }
      reject(new Error('Status code ' + code));
    });
  });
}

async function main() {
  console.log(`Create suite file ${ENVIRONMENT_FILENAME}.`);
  fs.writeFileSync(ENVIRONMENT_FILENAME, program.suite);

  console.log(`Render nginx configuration...`);
  await exec('./example/compose/nginx/portal/render.js');

  console.log(`Prepare environment with docker-compose...`);
  await exec('./scripts/utils/prepare-environment.sh');
  
  console.log('Start watching...');
  tsWatcher.on('add', reload);
  tsWatcher.on('remove', reload);
  tsWatcher.on('change', reload);
  
  startServer();
  startClient();
}

main()