'use strict';
const common = require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const { COPYFILE_FICLONE } = fs.constants;
const child_process = require('child_process');
const pkgName = 'foo';
const { addLibraryPath } = require('../common/shared-lib-util');

addLibraryPath(process.env);

if (process.argv[2] === 'child') {
  console.log(require(pkgName).string);
} else {
  const tmpdir = require('../common/tmpdir');
  tmpdir.refresh();

  // Copy node binary into a test $PREFIX directory.
  const prefixPath = tmpdir.resolve('install');
  fs.mkdirSync(prefixPath);
  let testExecPath;
  if (common.isWindows) {
    testExecPath = path.join(prefixPath, path.basename(process.execPath));
  } else {
    const prefixBinPath = path.join(prefixPath, 'bin');
    fs.mkdirSync(prefixBinPath);
    testExecPath = path.join(prefixBinPath, path.basename(process.execPath));
  }
  const mode = fs.statSync(process.execPath).mode;
  fs.copyFileSync(process.execPath, testExecPath, COPYFILE_FICLONE);
  fs.chmodSync(testExecPath, mode);

  const runTest = common.mustCallAtLeast((expectedString, env) => {
    const child = child_process.execFileSync(testExecPath,
                                             [ __filename, 'child' ],
                                             { encoding: 'utf8', env: env });
    assert.strictEqual(child.trim(), expectedString);
  });

  const testFixturesDir = fixtures.path(path.basename(__filename, '.js'));

  const env = { ...process.env };
  // Unset NODE_PATH.
  delete env.NODE_PATH;

  // Test empty global path.
  const noPkgHomeDir = tmpdir.resolve('home-no-pkg');
  fs.mkdirSync(noPkgHomeDir);
  env.HOME = env.USERPROFILE = noPkgHomeDir;
  assert.throws(
    () => {
      child_process.execFileSync(testExecPath, [ __filename, 'child' ],
                                 { encoding: 'utf8', env: env });
    },
    new RegExp(`Cannot find module '${RegExp.escape(pkgName)}'`));

  // Test module in $HOME/.node_modules.
  const modHomeDir = path.join(testFixturesDir, 'home-pkg-in-node_modules');
  env.HOME = env.USERPROFILE = modHomeDir;
  runTest('$HOME/.node_modules', env);

  // Test module in $HOME/.node_libraries.
  const libHomeDir = path.join(testFixturesDir, 'home-pkg-in-node_libraries');
  env.HOME = env.USERPROFILE = libHomeDir;
  runTest('$HOME/.node_libraries', env);

  // Test module both $HOME/.node_modules and $HOME/.node_libraries.
  const bothHomeDir = path.join(testFixturesDir, 'home-pkg-in-both');
  env.HOME = env.USERPROFILE = bothHomeDir;
  runTest('$HOME/.node_modules', env);

  // Test module in $PREFIX/lib/node.
  // Write module into $PREFIX/lib/node.
  const expectedString = '$PREFIX/lib/node';
  const prefixLibPath = path.join(prefixPath, 'lib');
  fs.mkdirSync(prefixLibPath);
  const prefixLibNodePath = path.join(prefixLibPath, 'node');
  fs.mkdirSync(prefixLibNodePath);
  const pkgPath = path.join(prefixLibNodePath, `${pkgName}.js`);
  fs.writeFileSync(pkgPath, `exports.string = '${expectedString}';`);

  env.HOME = env.USERPROFILE = noPkgHomeDir;
  runTest(expectedString, env);

  // Test module in all global folders.
  env.HOME = env.USERPROFILE = bothHomeDir;
  runTest('$HOME/.node_modules', env);

  // Test module in NODE_PATH is loaded ahead of global folders.
  env.HOME = env.USERPROFILE = bothHomeDir;
  env.NODE_PATH = path.join(testFixturesDir, 'node_path');
  runTest('$NODE_PATH', env);

  // Test module in local folder is loaded ahead of global folders.
  const localDir = path.join(testFixturesDir, 'local-pkg');
  env.HOME = env.USERPROFILE = bothHomeDir;
  env.NODE_PATH = path.join(testFixturesDir, 'node_path');
  const child = child_process.execFileSync(testExecPath,
                                           [ path.join(localDir, 'test.js') ],
                                           { encoding: 'utf8', env: env });
  assert.strictEqual(child.trim(), 'local');
}
