if (process.env.running_under_istanbul) return;

var readdir        = require("fs-readdir-recursive");
var helper         = require("mocha-fixtures");
var assert         = require("assert");
var rimraf         = require("rimraf");
var outputFileSync = require("output-file-sync");
var child          = require("child_process");
var path           = require("path");
var chai           = require("chai");
var fs             = require("fs");
var pathExists     = require("path-exists");
var _              = require("lodash");

var fixtureLoc = __dirname + "/fixtures";
var tmpLoc = __dirname + "/tmp";

var presetLocs = [
  __dirname + "/../../babel-preset-es2015",
  __dirname + "/../../babel-preset-react"
].join(",");

var pluginLocs = [
  __dirname + "/../../babel-plugin-transform-strict-mode",
  __dirname + "/../../babel-plugin-transform-es2015-modules-commonjs",
].join(",");

var readDir = function (loc) {
  var files = {};
  if (pathExists.sync(loc)) {
    _.each(readdir(loc), function (filename) {
      var contents = helper.readFile(loc + "/" + filename);
      files[filename] = contents;
    });
  }
  return files;
};

var saveInFiles = function (files) {
  _.each(files, function (content, filename) {
    outputFileSync(filename, content);
  });
};

var assertTest = function (stdout, stderr, opts) {
  var expectStderr = opts.stderr.trim();
  stderr = stderr.trim();

  if (opts.stderr) {
    if (opts.stderrContains) {
      assert.ok(_.contains(stderr, expectStderr), "stderr " + JSON.stringify(stderr) + " didn't contain " + JSON.stringify(expectStderr));
    } else {
      chai.expect(stderr).to.equal(expectStderr, "stderr didn't match");
    }
  } else if (stderr) {
    throw new Error("stderr:\n" + stderr);
  }

  var expectStdout = opts.stdout.trim();
  stdout = stdout.trim();
  stdout = stdout.replace(/\\/g, "/");

  if (opts.stdout) {
    if (opts.stdoutContains) {
      assert.ok(_.contains(stdout, expectStdout), "stdout " + JSON.stringify(stdout) + " didn't contain " + JSON.stringify(expectStdout));
    } else {
      chai.expect(stdout).to.equal(expectStdout, "stdout didn't match");
    }
  } else if (stdout) {
    throw new Error("stdout:\n" + stdout);
  }

  _.each(opts.outFiles, function (expect, filename) {
    var actual = helper.readFile(filename);
    chai.expect(actual).to.equal(expect, "out-file " + filename);
  });
};

var buildTest = function (binName, testName, opts) {
  var binLoc = path.normalize(__dirname + "/../../babel-cli/lib/" + binName);

  return function (callback) {
    this.timeout(5000);
    clear();
    saveInFiles(opts.inFiles);

    var args = [binLoc];

    if (binName !== "babel-external-helpers") {
      args.push("--presets", presetLocs, "--plugins", pluginLocs);
    }

    if (binName === "babel-node") {
      args.push("--only", "packages/*/test");
    }

    args = args.concat(opts.args);

    var spawn = child.spawn(process.execPath, args);

    var stderr = "";
    var stdout = "";

    spawn.stderr.on("data", function (chunk) {
      stderr += chunk;
    });

    spawn.stdout.on("data", function (chunk) {
      stdout += chunk;
    });

    spawn.on("close", function () {
      var err;

      try {
        assertTest(stdout, stderr, opts);
      } catch (e) {
        err = e;
      }

      if (err) {
        err.message = args.join(" ") + ": " + err.message;
      }

      callback(err);
    });

    if (opts.stdin) {
      spawn.stdin.write(opts.stdin);
      spawn.stdin.end();
    }
  };
};

var clear = function () {
  process.chdir(__dirname);
  if (pathExists.sync(tmpLoc)) rimraf.sync(tmpLoc);
  fs.mkdirSync(tmpLoc);
  process.chdir(tmpLoc);
};

_.each(fs.readdirSync(fixtureLoc), function (binName) {
  if (binName[0] === ".") return;

  var suiteLoc = fixtureLoc + "/" + binName;
  suite("bin/" + binName, function () {
    _.each(fs.readdirSync(fixtureLoc + "/" + binName), function (testName) {
      if (testName[0] === ".") return;

      var testLoc = suiteLoc + "/" + testName;

      var opts = {
        args: []
      };

      var optionsLoc = testLoc + "/options.json"
      if (pathExists.sync(optionsLoc)) _.merge(opts, require(optionsLoc));

      _.each(["stdout", "stdin", "stderr"], function (key) {
        var loc = testLoc + "/" + key + ".txt";
        if (pathExists.sync(loc)) {
          opts[key] = helper.readFile(loc);
        } else {
          opts[key] = opts[key] || "";
        }
      });

      opts.outFiles = readDir(testLoc + "/out-files");
      opts.inFiles  = readDir(testLoc + "/in-files");

      test(testName, buildTest(binName, testName, opts));
    });
  });
});
