/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * Note: This is a fork of the fb-specific transform.js
 */
'use strict';

const babel = require('babel-core');
const externalHelpersPlugin = require('babel-plugin-external-helpers');
const fs = require('fs');
const makeHMRConfig = require('babel-preset-react-native/configs/hmr');
const resolvePlugins = require('babel-preset-react-native/lib/resolvePlugins');
const inlineRequiresPlugin = require('babel-preset-fbjs/plugins/inline-requires');
const json5 = require('json5');
const path = require('path');

/**
 * Return a memoized function that checks for the existence of a
 * project level .babelrc file, and if it doesn't exist, reads the
 * default RN babelrc file and uses that.
 */
const getBabelRC = (function() {
  let babelRC = null;

  return function _getBabelRC(projectRoots) {
    if (babelRC !== null) {
      return babelRC;
    }

    babelRC = { plugins: [] }; // empty babelrc

    // Let's look for the .babelrc in the first project root.
    // In the future let's look into adding a command line option to specify
    // this location.
    //
    // NOTE: we're not reading the project's .babelrc here. We leave it up to
    // Babel to do that automatically and apply the transforms accordingly
    // (which works because we pass in `filename` and `sourceFilename` to
    // Babel when we transform).
    let projectBabelRCPath;
    if (projectRoots && projectRoots.length > 0) {
      projectBabelRCPath = path.resolve(projectRoots[0], '.babelrc');
    }

    // If a .babelrc file doesn't exist in the project,
    // use the Babel config provided with react-native.
    if (!projectBabelRCPath || !fs.existsSync(projectBabelRCPath)) {
      babelRC = json5.parse(
        fs.readFileSync(
          path.resolve(__dirname, 'react-packager', 'rn-babelrc.json'))
        );

      // Require the babel-preset's listed in the default babel config
      babelRC.presets = babelRC.presets.map((preset) => require('babel-preset-' + preset));
      babelRC.plugins = resolvePlugins(babelRC.plugins);
    }

    return babelRC;
  };
})();

/**
 * Given a filename and options, build a Babel
 * config object with the appropriate plugins.
 */
function buildBabelConfig(filename, options) {
  const babelRC = getBabelRC(options.projectRoots);

  const extraConfig = {
    filename,
    sourceFileName: filename,
  };

  let config = Object.assign({}, babelRC, extraConfig);

  // Add extra plugins
  const extraPlugins = [externalHelpersPlugin];

  var inlineRequires = options.inlineRequires;
  var blacklist = inlineRequires && inlineRequires.blacklist;
  if (inlineRequires && !(blacklist && filename in blacklist)) {
    extraPlugins.push(inlineRequiresPlugin);
  }

  config.plugins = extraPlugins.concat(config.plugins);

  if (options.hot) {
    const hmrConfig = makeHMRConfig(options, filename);
    config = Object.assign({}, config, hmrConfig);
  }

  return Object.assign({}, babelRC, config);
}

function transform(src, filename, options) {
  options = options || {};

  const OLD_BABEL_ENV = process.env.BABEL_ENV;
  process.env.BABEL_ENV = options.dev ? 'development' : 'production';

  try {
    const babelConfig = buildBabelConfig(filename, options);
    const result = babel.transform(src, babelConfig);

    return {
      ast: result.ast,
      code: result.code,
      map: result.map,
      filename: filename,
    };
  } finally {
    process.env.BABEL_ENV = OLD_BABEL_ENV;
  }
}

module.exports = function(data, callback) {
  let result;
  try {
    result = transform(data.sourceCode, data.filename, data.options);
  } catch (e) {
    callback(e);
    return;
  }

  callback(null, result);
};

// export for use in jest
module.exports.transform = transform;
