/* eslint-env node */

const path = require('path');
const webpack = require('webpack');
const MemoryFileSystem = require('memory-fs');
const EnhancedResolve = require('enhanced-resolve');
const transform = require('transform-jest-deps');
const babel = require('babel-jest');
const createConfig = require('../build/utils/createWebpackConfig');

const SyncNodeJsInputFileSystem = EnhancedResolve.SyncNodeJsInputFileSystem;

// Create test webpack config
const config = createConfig('test', {
  isTestRunner: true,
  platform: 'test'
});

// Store FS among preprocess calls
const memoryFs = new MemoryFileSystem();
const syncFs = new SyncNodeJsInputFileSystem();

// Filter all loaders
const shimLoaders = config.module.rules.filter(
  (rule) => rule.loader && rule.loader.indexOf('exports-loader') !== -1
);
const specialLoaders = config.module.rules.filter(
  (rule) => rule.loader && rule.loader.indexOf('raw-loader') !== -1
);
const babelLoaders = config.module.rules.filter(
  (rule) => rule.loader && rule.loader.indexOf('babel') >= 0
);

const aliasResolver = EnhancedResolve.create.sync(config.resolve);
function aliasPreprocessor(src, filename) {
  return transform(src, {
    sourceType: 'module'
  }, (name) => {
    try {
      const resolved = aliasResolver({}, process.cwd(), name);
      return path.normalize(resolved, process.cwd());
    } catch (e) {
      return name;
    }
  });
}

function webpackPreprocessor(filename) {
  const options = Object.assign(config, {
    entry: path.normalize(filename, process.cwd()),
    output: {
      path: '/',
      filename: filename,
      libraryTarget: 'commonjs2'
    },
    devtool: 'eval' // Fastest devtool
  });

  const compiler = webpack(options);
  compiler.outputFileSystem = memoryFs;
  compiler.inputFileSystem = syncFs;

  let stats = null;

  compiler.run((err, compilationStats) => {
    stats = compilationStats;
  });

  while (stats === null) {
    require('deasync').sleep(10);
  }

  return stats.compilation.assets[filename].source();
}

module.exports = {
  process: (src, filename) => {
    // This transforms with Babel all JS files that doesn't match `exclude`
    if (!babelLoaders.find((l) => new RegExp(l.exclude).test(filename))) {
      src = babel.process(src, filename);
    }

    // This transforms with Babel all JS files which are not in `node_modules`
    // Files in ``/app/js/libs/`` still need to be sent to aliasPreprocessor
    // because they might require modules aliased in webpack
    // e.g. `backbone` requires `jquery`
    if (!babelLoaders.find((l) => new RegExp(/node_modules[\/\\]/).test(filename))) {
      // Use aliases from webpack config
      src = aliasPreprocessor(src, filename);
    }

    const hasShims = shimLoaders.some(
      (loader) => new RegExp(loader.test).test(filename)
    );
    const hasSpecialRequires = specialLoaders.some(
      (loader) => src.match(loader.test)
    );
    if (hasShims || hasSpecialRequires) {
      // Use shims from webpack config
      src = webpackPreprocessor(filename);
    }

    return src;
  }
};