"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

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

function _bluebird() {
  const data = require("bluebird");

  _bluebird = function () {
    return data;
  };

  return data;
}

function _ramda() {
  const data = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

function _component() {
  const data = _interopRequireDefault(require("../../component"));

  _component = function () {
    return data;
  };

  return data;
}

function _() {
  const data = require("../..");

  _ = function () {
    return data;
  };

  return data;
}

function _utils() {
  const data = require("../../../utils");

  _utils = function () {
    return data;
  };

  return data;
}

function _sources() {
  const data = require("../../component/sources");

  _sources = function () {
    return data;
  };

  return data;
}

function _repositories() {
  const data = require("../../../scope/repositories");

  _repositories = function () {
    return data;
  };

  return data;
}

function _mergeFiles() {
  const data = _interopRequireDefault(require("../../../utils/merge-files"));

  _mergeFiles = function () {
    return data;
  };

  return data;
}

function _generalError() {
  const data = _interopRequireDefault(require("../../../error/general-error"));

  _generalError = function () {
    return data;
  };

  return data;
}

/**
 * it's easier to understand with an example.
 * a component bar/foo has two versions: 0.0.1, 0.0.2. Also, the component was modified locally.
 * the user is running 'bit checkout 0.0.1 bar/foo' to switch the version of bar/foo to 0.0.1.
 *
 * the goal is to rewrite bar/foo to the filesystem as 0.0.1 and keeping the local changes.
 * in other words, the changes the user did since 0.0.2 should be applied/merged on top of 0.0.1.
 *
 * to do the actual merge we use git, specifically `merge-file` command, so we try to use the same
 * terminology as git. From the command help:
 * `git merge-file <current-file> <base-file> <other-file>
 * git merge-file incorporates all changes that lead from the <base-file> to <other-file> into
 * <current-file>. The result ordinarily goes into <current-file>.`
 *
 * according to the example above:
 * current-file => bar/foo@0.0.1
 * base-file    => bar/foo@0.0.2
 * other-file   => bar/foo@0.0.2 + modification
 */
var _default =
/*#__PURE__*/
function () {
  var _threeWayMergeVersions = (0, _bluebird().coroutine)(function* ({
    consumer,
    otherComponent,
    otherVersion,
    currentComponent,
    currentVersion,
    baseComponent
  }) {
    const baseFiles = baseComponent.files;
    const currentFiles = currentComponent.files;
    const fsFiles = otherComponent.files;
    const results = {
      addFiles: [],
      modifiedFiles: [],
      unModifiedFiles: [],
      overrideFiles: [],
      hasConflicts: false
    };

    const getFileResult = (fsFile, baseFile, currentFile) => {
      const filePath = (0, _utils().pathNormalizeToLinux)(fsFile.relative);

      if (!currentFile) {
        // if !currentFile && !baseFile, the file was created after the last tag
        // if !currentFile && baseFile,  the file was created as part of the last tag
        // either way, no need to do any calculation, the file should be added
        results.addFiles.push({
          filePath,
          fsFile
        });
        return;
      }

      if (!baseFile) {
        // if currentFile && !baseFile, the file was deleted as part of the last tag
        results.overrideFiles.push({
          filePath,
          fsFile
        });
        return;
      }

      const fsFileHash = (0, _utils().sha1)(fsFile.contents);
      const baseFileHash = (0, _utils().sha1)(baseFile.contents);
      const currentFileHash = (0, _utils().sha1)(currentFile.contents);

      if (fsFileHash === currentFileHash) {
        // no need to check also for fsFileHash === baseFileHash, as long as fs == current, no need to take any action
        results.unModifiedFiles.push({
          filePath,
          fsFile
        });
        return;
      }

      if (fsFileHash === baseFileHash) {
        results.overrideFiles.push({
          filePath,
          fsFile
        });
        return;
      } // it was changed in both, there is a chance for conflict


      fsFile.version = otherVersion; // $FlowFixMe it's a hack to pass the data, version is not a valid attribute.

      baseFile.version = otherVersion; // $FlowFixMe it's a hack to pass the data, version is not a valid attribute.

      currentFile.version = currentVersion;
      results.modifiedFiles.push({
        filePath,
        fsFile,
        baseFile,
        currentFile,
        output: null,
        conflict: null
      });
    };

    fsFiles.forEach(fsFile => {
      const baseFile = baseFiles.find(file => file.relative === fsFile.relative);
      const currentFile = currentFiles.find(file => file.relative === fsFile.relative);
      getFileResult(fsFile, baseFile, currentFile);
    });
    if (_ramda().default.isEmpty(results.modifiedFiles)) return results;
    const conflictResults = yield getMergeResults(consumer, results.modifiedFiles);
    conflictResults.forEach(conflictResult => {
      const modifiedFile = results.modifiedFiles.find(file => file.filePath === conflictResult.filePath);
      if (!modifiedFile) throw new (_generalError().default)(`unable to find ${conflictResult.filePath} in modified files array`);
      modifiedFile.output = conflictResult.output;
      modifiedFile.conflict = conflictResult.conflict;
      if (conflictResult.conflict) results.hasConflicts = true;
    });
    return results;
  });

  function threeWayMergeVersions(_x) {
    return _threeWayMergeVersions.apply(this, arguments);
  }

  return threeWayMergeVersions;
}();

exports.default = _default;

function getMergeResults(_x2, _x3) {
  return _getMergeResults.apply(this, arguments);
}

function _getMergeResults() {
  _getMergeResults = (0, _bluebird().coroutine)(function* (consumer, modifiedFiles) {
    const tmp = new (_repositories().Tmp)(consumer.scope);
    const conflictResultsP = modifiedFiles.map(
    /*#__PURE__*/
    function () {
      var _ref = (0, _bluebird().coroutine)(function* (modifiedFile) {
        const fsFilePathP = tmp.save(modifiedFile.fsFile.contents);

        const writeFile =
        /*#__PURE__*/
        function () {
          var _ref2 = (0, _bluebird().coroutine)(function* (file) {
            return tmp.save(file.contents);
          });

          return function writeFile(_x5) {
            return _ref2.apply(this, arguments);
          };
        }();

        const baseFilePathP = writeFile(modifiedFile.baseFile);
        const currentFilePathP = writeFile(modifiedFile.currentFile);
        const [fsFilePath, baseFilePath, currentFilePath] = yield Promise.all([fsFilePathP, baseFilePathP, currentFilePathP]);
        const mergeFilesParams = {
          filePath: modifiedFile.filePath,
          currentFile: {
            // $FlowFixMe
            label: modifiedFile.currentFile.version,
            path: currentFilePath
          },
          baseFile: {
            path: baseFilePath
          },
          otherFile: {
            label: `${modifiedFile.fsFile.version} modified`,
            path: fsFilePath
          }
        };
        return (0, _mergeFiles().default)(mergeFilesParams);
      });

      return function (_x4) {
        return _ref.apply(this, arguments);
      };
    }());
    return Promise.all(conflictResultsP);
  });
  return _getMergeResults.apply(this, arguments);
}