'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _regenerator;

function _load_regenerator() {
  return _regenerator = _interopRequireDefault(require('babel-runtime/regenerator'));
}

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var getMergeResults = function () {
  var _ref3 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(consumer, modifiedFiles) {
    var _this = this;

    var tmp, conflictResultsP, conflictResults;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
      while (1) {
        switch (_context4.prev = _context4.next) {
          case 0:
            tmp = new (_repositories || _load_repositories()).Tmp(consumer.scope);
            conflictResultsP = modifiedFiles.map(function () {
              var _ref4 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(modifiedFile) {
                var fsFilePathP, writeFile, baseFilePathP, currentFilePathP, _ref6, _ref7, fsFilePath, baseFilePath, currentFilePath, mergeFilesParams;

                return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
                  while (1) {
                    switch (_context3.prev = _context3.next) {
                      case 0:
                        fsFilePathP = tmp.save(modifiedFile.fsFile.contents);

                        writeFile = function () {
                          var _ref5 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(file) {
                            var content;
                            return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
                              while (1) {
                                switch (_context2.prev = _context2.next) {
                                  case 0:
                                    _context2.next = 2;
                                    return file.file.load(consumer.scope.objects);

                                  case 2:
                                    content = _context2.sent;
                                    return _context2.abrupt('return', tmp.save(content));

                                  case 4:
                                  case 'end':
                                    return _context2.stop();
                                }
                              }
                            }, _callee2, _this);
                          }));

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

                        baseFilePathP = writeFile(modifiedFile.baseFile);
                        currentFilePathP = writeFile(modifiedFile.currentFile);
                        _context3.next = 6;
                        return Promise.all([fsFilePathP, baseFilePathP, currentFilePathP]);

                      case 6:
                        _ref6 = _context3.sent;
                        _ref7 = _slicedToArray(_ref6, 3);
                        fsFilePath = _ref7[0];
                        baseFilePath = _ref7[1];
                        currentFilePath = _ref7[2];
                        mergeFilesParams = {
                          filePath: modifiedFile.filePath,
                          currentFile: {
                            // $FlowFixMe
                            label: modifiedFile.currentFile.version,
                            path: currentFilePath
                          },
                          baseFile: {
                            path: baseFilePath
                          },
                          otherFile: {
                            label: `${modifiedFile.fsFile.version} modified`,
                            path: fsFilePath
                          }
                        };
                        return _context3.abrupt('return', (0, (_mergeFiles || _load_mergeFiles()).default)(mergeFilesParams));

                      case 13:
                      case 'end':
                        return _context3.stop();
                    }
                  }
                }, _callee3, _this);
              }));

              return function (_x4) {
                return _ref4.apply(this, arguments);
              };
            }());
            _context4.prev = 2;
            _context4.next = 5;
            return Promise.all(conflictResultsP);

          case 5:
            conflictResults = _context4.sent;
            _context4.next = 8;
            return tmp.clear();

          case 8:
            return _context4.abrupt('return', conflictResults);

          case 11:
            _context4.prev = 11;
            _context4.t0 = _context4['catch'](2);
            _context4.next = 15;
            return tmp.clear();

          case 15:
            throw _context4.t0;

          case 16:
          case 'end':
            return _context4.stop();
        }
      }
    }, _callee4, this, [[2, 11]]);
  }));

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

var _ramda;

function _load_ramda() {
  return _ramda = _interopRequireDefault(require('ramda'));
}

var _component;

function _load_component() {
  return _component = _interopRequireDefault(require('../../component'));
}

var _models;

function _load_models() {
  return _models = require('../../../scope/models');
}

var _;

function _load_() {
  return _ = require('../..');
}

var _utils;

function _load_utils() {
  return _utils = require('../../../utils');
}

var _sources;

function _load_sources() {
  return _sources = require('../../component/sources');
}

var _repositories;

function _load_repositories() {
  return _repositories = require('../../../scope/repositories');
}

var _mergeFiles;

function _load_mergeFiles() {
  return _mergeFiles = _interopRequireDefault(require('../../../utils/merge-files'));
}

var _generalError;

function _load_generalError() {
  return _generalError = _interopRequireDefault(require('../../../error/general-error'));
}

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

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

/**
 * 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
 */
exports.default = function () {
  var _ref2 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(_ref) {
    var consumer = _ref.consumer,
        otherComponent = _ref.otherComponent,
        otherVersion = _ref.otherVersion,
        currentComponent = _ref.currentComponent,
        currentVersion = _ref.currentVersion,
        baseComponent = _ref.baseComponent;
    var baseFiles, currentFiles, fsFiles, results, getFileResult, conflictResults;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            // baseFiles and currentFiles come from the model, therefore their paths include the
            // sharedOriginallyDir. fsFiles come from the Fs, therefore their paths don't include the
            // sharedOriginallyDir.
            // option 1) strip sharedOriginallyDir from baseFiles and currentFiles. the problem is that the
            // sharedDir can be different if the dependencies were changes for example, as a result, it won't
            // be possible to compare between the files as the paths are different.
            baseFiles = baseComponent.files;
            currentFiles = currentComponent.files;
            fsFiles = otherComponent.cloneFilesWithSharedDir();
            results = { addFiles: [], modifiedFiles: [], unModifiedFiles: [], overrideFiles: [], hasConflicts: false };

            getFileResult = function getFileResult(fsFile, baseFile, currentFile) {
              var filePath = (0, (_utils || _load_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;
              }
              var fsFileHash = (0, (_utils || _load_utils()).sha1)(fsFile.contents);
              var baseFileHash = baseFile.file.hash;
              var currentFileHash = currentFile.file.hash;
              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(function (fsFile) {
              var relativePath = (0, (_utils || _load_utils()).pathNormalizeToLinux)(fsFile.relative);
              var baseFile = baseFiles.find(function (file) {
                return file.relativePath === relativePath;
              });
              var currentFile = currentFiles.find(function (file) {
                return file.relativePath === relativePath;
              });
              getFileResult(fsFile, baseFile, currentFile);
            });

            if (!(_ramda || _load_ramda()).default.isEmpty(results.modifiedFiles)) {
              _context.next = 8;
              break;
            }

            return _context.abrupt('return', results);

          case 8:
            _context.next = 10;
            return getMergeResults(consumer, results.modifiedFiles);

          case 10:
            conflictResults = _context.sent;

            conflictResults.forEach(function (conflictResult) {
              var modifiedFile = results.modifiedFiles.find(function (file) {
                return file.filePath === conflictResult.filePath;
              });
              if (!modifiedFile) throw new (_generalError || _load_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 _context.abrupt('return', results);

          case 13:
          case 'end':
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

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

  return threeWayMergeVersions;
}();