"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getReadAndWriteStreams = getReadAndWriteStreams;
exports.searchAndReplace = void 0;

var _fs = _interopRequireDefault(require("fs"));

var _os = _interopRequireDefault(require("os"));

var _path = _interopRequireDefault(require("path"));

var _chalk = _interopRequireDefault(require("chalk"));

var _debug = _interopRequireDefault(require("debug"));

var _vipSearchReplace = require("@automattic/vip-search-replace");

var _tracker = require("./tracker");

var _prompt = require("./cli/prompt");

var _clientFileUploader = require("./client-file-uploader");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * 
 * @format
 */

/**
 * External dependencies
 */

/**
 * Internal dependencies
 */
const debug = (0, _debug.default)('@automattic/vip:lib:search-and-replace');

const flatten = arr => {
  return arr.reduce(function (flat, toFlatten) {
    return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
  }, []);
};

function makeTempDir() {
  const tmpDir = _fs.default.mkdtempSync(_path.default.join(_os.default.tmpdir(), 'vip-search-replace-'));

  debug(`Created a directory to hold temporary files: ${tmpDir}`);
  return tmpDir;
}

function getReadAndWriteStreams({
  fileName,
  inPlace,
  output
}) {
  let writeStream;
  let usingStdOut = false;
  let outputFileName;

  if (inPlace) {
    const midputFileName = _path.default.join(makeTempDir(), _path.default.basename(fileName));

    _fs.default.copyFileSync(fileName, midputFileName);

    debug(`Copied input file to ${midputFileName}`);
    console.log(`Searching ${_chalk.default.cyan(fileName)}`);
    debug(`Set output to the original file path ${fileName}`);
    console.log('Replacing...');
    outputFileName = fileName;
    return {
      outputFileName,
      readStream: _fs.default.createReadStream(midputFileName),
      usingStdOut,
      writeStream: _fs.default.createWriteStream(fileName)
    };
  }

  debug(`Reading input from file: ${fileName}`);
  console.log(`Searching file ${_chalk.default.cyan(fileName)}`);

  switch (typeof output) {
    case 'string':
      writeStream = _fs.default.createWriteStream(output);
      outputFileName = output;
      debug(`Outputting to file: ${outputFileName}`);
      console.log('Replacing...');
      break;

    case 'object':
      writeStream = output;

      if (writeStream === process.stdout) {
        usingStdOut = true;
        debug('Outputting to the standard output stream');
      } else {
        debug('Outputting to the provided output stream');
      }

      break;

    default:
      const tmpOutFile = _path.default.join(makeTempDir(), _path.default.basename(fileName));

      writeStream = _fs.default.createWriteStream(tmpOutFile);
      outputFileName = tmpOutFile;
      debug(`Outputting to file: ${outputFileName}`);
      console.log('Replacing...');
      break;
  }

  return {
    outputFileName,
    readStream: _fs.default.createReadStream(fileName),
    usingStdOut,
    writeStream
  };
}

const searchAndReplace = async (fileName, pairs, {
  isImport = true,
  inPlace = false,
  output = process.stdout
}, binary = null) => {
  console.log('Starting Search and Replace...');
  await (0, _tracker.trackEvent)('searchreplace_started', {
    is_import: isImport,
    in_place: inPlace
  });
  const startTime = process.hrtime();
  const fileSize = (0, _clientFileUploader.getFileSize)(fileName); // if we don't have any pairs to replace with, return the input file

  if (!pairs || !pairs.length) {
    throw new Error('No search and replace parameters provided.');
  } // If only one pair is provided, ensure we have an array


  if (!Array.isArray(pairs)) {
    pairs = [pairs];
  } // determine all the replacements required


  const replacementsArr = pairs.map(str => str.split(',').map(s => s.trim()));
  const replacements = flatten(replacementsArr);
  debug('Pairs: ', pairs, 'Replacements: ', replacements); // Add a confirmation step for search-replace

  const yes = await (0, _prompt.confirm)([{
    key: 'From',
    value: `${_chalk.default.cyan(replacements[0])}`
  }, {
    key: 'To',
    value: `${_chalk.default.cyan(replacements[1])}`
  }], 'Proceed with the following values?'); // Bail if user does not wish to proceed

  if (!yes) {
    console.log(`${_chalk.default.red('Cancelling')}`);
    await (0, _tracker.trackEvent)('search_replace_cancelled', {
      is_import: isImport,
      in_place: inPlace
    });
    process.exit();
  }

  if (inPlace) {
    const approved = await (0, _prompt.confirm)([], 'Are you sure you want to run search and replace on your input file? This operation is not reversible.'); // Bail if user does not wish to proceed

    if (!approved) {
      console.log(`${_chalk.default.red('Cancelling')}`);
      await (0, _tracker.trackEvent)('search_replace_in_place_cancelled', {
        is_import: isImport,
        in_place: inPlace
      });
      process.exit();
    }
  }

  const {
    usingStdOut,
    outputFileName,
    readStream,
    writeStream
  } = getReadAndWriteStreams({
    fileName,
    inPlace,
    output
  });
  const replacedStream = await (0, _vipSearchReplace.replace)(readStream, replacements, binary);
  const result = await new Promise((resolve, reject) => {
    replacedStream.pipe(writeStream).on('finish', () => {
      if (!usingStdOut) {
        console.log();
        console.log(`${'Search and Replace Complete!'}`); // Only log this message if the output SQL file isn't the original input file

        const message = `Your new SQL file has been saved to ${_chalk.default.cyan(outputFileName)}`;
        inPlace ? '' : console.log(message);
        console.log();
      }

      resolve({
        inputFileName: fileName,
        outputFileName,
        usingStdOut
      });
    }).on('error', () => {
      console.log(_chalk.default.red("Oh no! We couldn't write to the output file.  Please check your available disk space and file/folder permissions."));
      reject();
    });
  });
  const endTime = process.hrtime(startTime);
  const end = endTime[1] / 1000000; // time in ms

  await (0, _tracker.trackEvent)('searchreplace_completed', {
    time_to_run: end,
    file_size: fileSize
  });
  return result;
};

exports.searchAndReplace = searchAndReplace;