/* @flow */

import type { Context } from '../../types.js';

const fs = require('fs');
const spawn = require('cross-spawn').spawn;
const path = require('path');

// This is a necessary hack to find Jest depending if node_modules has flatten dependencies or not
const JEST_PATH = `${require.resolve('jest').split('jest')[0]}.bin/jest`;
const JEST_DEBUG_CONFIG_PATH = path.join(__dirname, 'jestEnvironmentNodeDebug.js');
const TEST_MOCKS_PATH = `${path.join(__dirname)}`;

const getJestDefaultConfig = (aliases, webpackRules) => {
  const moduleNameMapper = {};

  // Handling Static Assets = mock them out
  const fileRegex = new RegExp(
    `${webpackRules[3].source}|${webpackRules[4].source}|${webpackRules[5].source}`,
  );
  moduleNameMapper[fileRegex.source] = `${TEST_MOCKS_PATH}/fileMock.js`;
  const stylesRegex = new RegExp(`${webpackRules[1].source}|${webpackRules[2].source}`);
  moduleNameMapper[stylesRegex.source] = `${TEST_MOCKS_PATH}/styleMock.js`;

  // We map webpack aliases from webpack-isomorphic-tools-config file
  // so Jest can detect them in tests too
  Object.keys(aliases).forEach((key) => {
    moduleNameMapper[`^${key}(.*)$`] = `${aliases[key]}$1`;
  });

  const roots = ['src'];
  if (fs.existsSync(path.join(process.cwd(), 'test'))) {
    // Previous to gluestick 1.0, projects used to have test folder, if that folder is not present
    // and we add it to the roots, the watch mode of Jest will fail: ENOENT
    roots[roots.length] = 'test';
  }

  const config = {
    moduleNameMapper,
    roots,
    transformIgnorePatterns: [
      '/node_modules/(?!gluestick-addon-)',
    ],
    // Clear watchman warnings (including the one of `test` folder non-existing for new projects)
    // Might be worth in the future to come back here and see if there is a really difference
    // using watchman or not
    watchman: false,
  };

  const argv = [];
  argv.push('--config', JSON.stringify(config));
  return argv;
};

const getDebugDefaultConfig = (aliases, webpackRules) => {
  const argv = [];
  argv.push('--inspect');
  argv.push('--debug-brk');
  argv.push(JEST_PATH);
  argv.push('--env');
  argv.push(JEST_DEBUG_CONFIG_PATH);
  argv.push(...getJestDefaultConfig(aliases, webpackRules));
  argv.push('-i');
  argv.push('--watch');
  return argv;
};

module.exports = (context: Context, options: { [key: string]: Object }) => {
  const spawnOptions = {
    stdio: 'inherit',
  };
  // $FlowFixMe
  const aliases: Object = context.config.webpackConfig.client.resolve.alias;
  // $FlowFixMe
  const webpackRules: Object = context.config.webpackConfig.client.module.rules.map(
    rule => rule.test,
  );
  if (options.debugTest) {
    const argvDebug = getDebugDefaultConfig(aliases, webpackRules);
    spawn.sync('node', argvDebug, spawnOptions);
  } else {
    const jest = require('jest');
    const argv = getJestDefaultConfig(aliases, webpackRules)
      .concat(options.parent.rawArgs.slice(3));
    // Since we require Jest programmatically, we need to make sure
    // to set NODE_ENV='test' when running it
    process.env.NODE_ENV = 'test';
    jest.run(argv);
  }
};

@@ -1989,32 +1989,41 @@
 es, webpackRules
+, options
 ) =%3E %7B%0A  const a
@@ -2271,35 +2271,417 @@
 -watch');%0A  
-return argv
+// Exclude those options to avoid dublication.%0A  const optionsToExclude = %5B'-D', '--debug-test', '-i', '--runInBand', '--watch', '--config', '-c'%5D;%0A  return argv.concat(%0A    options.filter((option: string): boolean =%3E %7B%0A      return optionsToExclude.findIndex((optionToExclude: string): boolean =%3E %7B%0A        return new RegExp(%60%5E$%7BoptionToExclude%7D.*%60).test(option);%0A      %7D) === -1;%0A    %7D),%0A  )
 ;%0A%7D;%0A%0Amodule
@@ -3025,16 +3025,70 @@
 t,%0A  );%0A
+  const rawOptions = options.parent.rawArgs.slice(3);%0A
   if (op
@@ -3171,16 +3171,28 @@
 ackRules
+, rawOptions
 );%0A    s
@@ -3344,23 +3344,16 @@
 les)
-%0A      
 .concat(
 opti
@@ -3352,39 +3352,18 @@
 cat(
-o
+rawO
 ptions
-.parent.rawArgs.slice(3)
 );%0A 

support all jest options in debug mode