'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getMergeStrategyInteractive = exports.mergeVersion = exports.FileStatus = exports.MergeOptions = exports.mergeOptionsCli = undefined;

var _regenerator;

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

var mergeVersion = exports.mergeVersion = function () {
  var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(consumer, version, ids, mergeStrategy) {
    var _ref2, components, componentsStatusP, componentsStatus, componentWithConflict, mergedComponentsP, mergedComponents;

    return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return consumer.loadComponents(ids);

          case 2:
            _ref2 = _context.sent;
            components = _ref2.components;
            componentsStatusP = components.map(function (component) {
              return getComponentStatus(consumer, component, version);
            });
            _context.next = 7;
            return Promise.all(componentsStatusP);

          case 7:
            componentsStatus = _context.sent;
            componentWithConflict = componentsStatus.find(function (component) {
              return component.mergeResults.hasConflicts;
            });

            if (!(componentWithConflict && !mergeStrategy)) {
              _context.next = 13;
              break;
            }

            _context.next = 12;
            return getMergeStrategyInteractive();

          case 12:
            mergeStrategy = _context.sent;

          case 13:
            mergedComponentsP = componentsStatus.map(function (_ref3) {
              var id = _ref3.id,
                  componentFromFS = _ref3.componentFromFS,
                  mergeResults = _ref3.mergeResults;

              return applyVersion(consumer, id, componentFromFS, mergeResults, mergeStrategy);
            });
            _context.next = 16;
            return Promise.all(mergedComponentsP);

          case 16:
            mergedComponents = _context.sent;
            return _context.abrupt('return', { components: mergedComponents, version });

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

  return function mergeVersion(_x, _x2, _x3, _x4) {
    return _ref.apply(this, arguments);
  };
}();

var getComponentStatus = function () {
  var _ref4 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(consumer, component, version) {
    var componentModel, existingBitMapId, currentlyUsedVersion, otherComponent, mergeResults;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            _context2.next = 2;
            return consumer.scope.sources.get(component.id);

          case 2:
            componentModel = _context2.sent;

            if (componentModel) {
              _context2.next = 5;
              break;
            }

            throw new (_generalError || _load_generalError()).default(`component ${component.id.toString()} doesn't have any version yet`);

          case 5:
            if (componentModel.hasVersion(version)) {
              _context2.next = 7;
              break;
            }

            throw new (_generalError || _load_generalError()).default(`component ${component.id.toStringWithoutVersion()} doesn't have version ${version}`);

          case 7:
            existingBitMapId = consumer.bitMap.getExistingComponentId(component.id.toStringWithoutVersion());
            currentlyUsedVersion = (_bitId || _load_bitId()).BitId.parse(existingBitMapId).version;

            if (!(currentlyUsedVersion === version)) {
              _context2.next = 11;
              break;
            }

            throw new (_generalError || _load_generalError()).default(`component ${component.id.toStringWithoutVersion()} is already at version ${version}`);

          case 11:
            _context2.next = 13;
            return componentModel.loadVersion(version, consumer.scope.objects);

          case 13:
            otherComponent = _context2.sent;
            _context2.next = 16;
            return (0, (_twoWayMerge || _load_twoWayMerge()).default)({
              consumer,
              otherComponent,
              otherVersion: version,
              currentComponent: component,
              currentVersion: currentlyUsedVersion
            });

          case 16:
            mergeResults = _context2.sent;
            return _context2.abrupt('return', { componentFromFS: component, id: component.id, mergeResults });

          case 18:
          case 'end':
            return _context2.stop();
        }
      }
    }, _callee2, this);
  }));

  return function getComponentStatus(_x5, _x6, _x7) {
    return _ref4.apply(this, arguments);
  };
}();

/**
 * it doesn't matter whether the component is modified. the idea is to merge the
 * specified version with the current version.
 *
 * 1) when there are conflicts and the strategy is "ours", don't do any change to the component.
 *
 * 2) when there are conflicts and the strategy is "theirs", add all files from the specified
 * version and write the component.
 *
 * 3) when there is no conflict or there are conflicts and the strategy is manual, update
 * component.files.
 *
 * it's going to be 2-way merge:
 * current-file: is the current file.
 * base-file: empty.
 * other-file: the specified version.
 */


var applyVersion = function () {
  var _ref5 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(consumer, id, componentFromFS, mergeResults, mergeStrategy) {
    var filesStatus, component, componentMap, files, modifiedStatus, filesStatusNoSharedDir, modifiedStatusNoSharedDir;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            filesStatus = {};

            if (!(mergeResults.hasConflicts && mergeStrategy === MergeOptions.ours)) {
              _context3.next = 4;
              break;
            }

            componentFromFS.files.forEach(function (file) {
              filesStatus[(0, (_path2 || _load_path2()).pathNormalizeToLinux)(file.relative)] = FileStatus.unchanged;
            });
            return _context3.abrupt('return', { id, filesStatus });

          case 4:
            component = componentFromFS.componentFromModel;

            if (component) {
              _context3.next = 7;
              break;
            }

            throw new (_generalError || _load_generalError()).default('failed finding the component in the model');

          case 7:
            componentMap = componentFromFS.componentMap;

            if (componentMap) {
              _context3.next = 10;
              break;
            }

            throw new (_generalError || _load_generalError()).default('applyVersion: componentMap was not found');

          case 10:
            files = componentFromFS.cloneFilesWithSharedDir();

            component.files = files;

            files.forEach(function (file) {
              filesStatus[(0, (_path2 || _load_path2()).pathNormalizeToLinux)(file.relative)] = FileStatus.unchanged;
            });

            // update files according to the merge results
            _context3.next = 15;
            return applyModifiedVersion(consumer, files, mergeResults, mergeStrategy);

          case 15:
            modifiedStatus = _context3.sent;


            if (componentMap.origin === (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED) {
              component.originallySharedDir = componentMap.originallySharedDir || null;
              component.stripOriginallySharedDir(consumer.bitMap);
            }

            _context3.next = 19;
            return component.write({
              override: true,
              writeBitJson: false, // never override the existing bit.json
              writePackageJson: false,
              deleteBitDirContent: false,
              origin: componentMap.origin,
              consumer,
              componentMap
            });

          case 19:

            consumer.bitMap.removeComponent(component.id);
            component._addComponentToBitMap(consumer.bitMap, componentMap.rootDir, componentMap.origin);

            filesStatusNoSharedDir = filesStatusWithoutSharedDir(filesStatus, component, componentMap);
            modifiedStatusNoSharedDir = filesStatusWithoutSharedDir(modifiedStatus, component, componentMap);
            return _context3.abrupt('return', { id, filesStatus: Object.assign(filesStatusNoSharedDir, modifiedStatusNoSharedDir) });

          case 24:
          case 'end':
            return _context3.stop();
        }
      }
    }, _callee3, this);
  }));

  return function applyVersion(_x8, _x9, _x10, _x11, _x12) {
    return _ref5.apply(this, arguments);
  };
}();

var applyModifiedVersion = function () {
  var _ref6 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee6(consumer, componentFiles, mergeResults, mergeStrategy) {
    var _this = this;

    var filesStatus, modifiedP, addFilesP;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee6$(_context6) {
      while (1) {
        switch (_context6.prev = _context6.next) {
          case 0:
            filesStatus = {};
            modifiedP = mergeResults.modifiedFiles.map(function () {
              var _ref7 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(file) {
                var filePath, foundFile, otherFile, content;
                return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
                  while (1) {
                    switch (_context4.prev = _context4.next) {
                      case 0:
                        filePath = (_path || _load_path()).default.normalize(file.filePath);
                        foundFile = componentFiles.find(function (componentFile) {
                          return componentFile.relative === filePath;
                        });

                        if (foundFile) {
                          _context4.next = 4;
                          break;
                        }

                        throw new (_generalError || _load_generalError()).default(`file ${filePath} not found`);

                      case 4:
                        if (!(mergeResults.hasConflicts && mergeStrategy === MergeOptions.theirs)) {
                          _context4.next = 13;
                          break;
                        }

                        // write the version of otherFile
                        otherFile = file.otherFile;
                        // $FlowFixMe

                        _context4.next = 8;
                        return otherFile.file.load(consumer.scope.objects);

                      case 8:
                        content = _context4.sent;

                        foundFile.contents = content.contents;
                        filesStatus[file.filePath] = FileStatus.updated;
                        _context4.next = 24;
                        break;

                      case 13:
                        if (!file.conflict) {
                          _context4.next = 18;
                          break;
                        }

                        foundFile.contents = Buffer.from(file.conflict);
                        filesStatus[file.filePath] = FileStatus.manual;
                        _context4.next = 24;
                        break;

                      case 18:
                        if (!file.output) {
                          _context4.next = 23;
                          break;
                        }

                        foundFile.contents = Buffer.from(file.output);
                        filesStatus[file.filePath] = FileStatus.merged;
                        _context4.next = 24;
                        break;

                      case 23:
                        throw new (_generalError || _load_generalError()).default('file does not have output nor conflict');

                      case 24:
                      case 'end':
                        return _context4.stop();
                    }
                  }
                }, _callee4, _this);
              }));

              return function (_x17) {
                return _ref7.apply(this, arguments);
              };
            }());
            addFilesP = mergeResults.addFiles.map(function () {
              var _ref8 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5(file) {
                var otherFile, newFile;
                return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
                  while (1) {
                    switch (_context5.prev = _context5.next) {
                      case 0:
                        otherFile = file.otherFile;
                        _context5.next = 3;
                        return (_sources || _load_sources()).SourceFile.loadFromSourceFileModel(otherFile, consumer.scope.objects);

                      case 3:
                        newFile = _context5.sent;

                        componentFiles.push(newFile);
                        filesStatus[file.filePath] = FileStatus.added;

                      case 6:
                      case 'end':
                        return _context5.stop();
                    }
                  }
                }, _callee5, _this);
              }));

              return function (_x18) {
                return _ref8.apply(this, arguments);
              };
            }());
            _context6.next = 5;
            return Promise.all([Promise.all(modifiedP), Promise.all(addFilesP)]);

          case 5:
            return _context6.abrupt('return', filesStatus);

          case 6:
          case 'end':
            return _context6.stop();
        }
      }
    }, _callee6, this);
  }));

  return function applyModifiedVersion(_x13, _x14, _x15, _x16) {
    return _ref6.apply(this, arguments);
  };
}();

var getMergeStrategyInteractive = exports.getMergeStrategyInteractive = function () {
  var _ref9 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee7() {
    var result;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee7$(_context7) {
      while (1) {
        switch (_context7.prev = _context7.next) {
          case 0:
            _context7.prev = 0;
            _context7.next = 3;
            return (0, (_prompts || _load_prompts()).resolveConflictPrompt)();

          case 3:
            result = _context7.sent;
            return _context7.abrupt('return', mergeOptionsCli[result.mergeStrategy]);

          case 7:
            _context7.prev = 7;
            _context7.t0 = _context7['catch'](0);
            throw new (_generalError || _load_generalError()).default('the action has been canceled');

          case 10:
          case 'end':
            return _context7.stop();
        }
      }
    }, _callee7, this, [[0, 7]]);
  }));

  return function getMergeStrategyInteractive() {
    return _ref9.apply(this, arguments);
  };
}();

exports.getMergeStrategy = getMergeStrategy;
exports.filesStatusWithoutSharedDir = filesStatusWithoutSharedDir;

var _path;

function _load_path() {
  return _path = _interopRequireDefault(require('path'));
}

var _chalk;

function _load_chalk() {
  return _chalk = _interopRequireDefault(require('chalk'));
}

var _bitId;

function _load_bitId() {
  return _bitId = require('../../../bit-id');
}

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 _sources;

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

var _prompts;

function _load_prompts() {
  return _prompts = require('../../../prompts');
}

var _path2;

function _load_path2() {
  return _path2 = require('../../../utils/path');
}

var _twoWayMerge;

function _load_twoWayMerge() {
  return _twoWayMerge = _interopRequireDefault(require('./two-way-merge'));
}

var _constants;

function _load_constants() {
  return _constants = require('../../../constants');
}

var _generalError;

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

var _componentMap;

function _load_componentMap() {
  return _componentMap = _interopRequireDefault(require('../../bit-map/component-map'));
}

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"); }); }; }

var mergeOptionsCli = exports.mergeOptionsCli = { o: 'ours', t: 'theirs', m: 'manual' };
var MergeOptions = exports.MergeOptions = { ours: 'ours', theirs: 'theirs', manual: 'manual' };
var FileStatus = exports.FileStatus = {
  merged: (_chalk || _load_chalk()).default.green('auto-merged'),
  manual: (_chalk || _load_chalk()).default.red('CONFLICT'),
  updated: (_chalk || _load_chalk()).default.green('updated'),
  added: (_chalk || _load_chalk()).default.green('added'),
  overridden: (_chalk || _load_chalk()).default.yellow('overridden'),
  unchanged: (_chalk || _load_chalk()).default.green('unchanged')
};
function getMergeStrategy(ours, theirs, manual) {
  if (ours && theirs || ours && manual || theirs && manual) {
    throw new (_generalError || _load_generalError()).default('please choose only one of the following: ours, theirs or manual');
  }
  if (ours) return MergeOptions.ours;
  if (theirs) return MergeOptions.theirs;
  if (manual) return MergeOptions.manual;
  return null;
}

function filesStatusWithoutSharedDir(filesStatus, component, componentMap) {
  if (componentMap.origin !== (_constants || _load_constants()).COMPONENT_ORIGINS.IMPORTED) return filesStatus;
  component.setOriginallySharedDir();
  if (!component.originallySharedDir) return filesStatus;
  var sharedDir = component.originallySharedDir;
  var fileWithoutSharedDir = function fileWithoutSharedDir(file) {
    return file.replace(`${sharedDir}/`, '');
  };
  return Object.keys(filesStatus).reduce(function (acc, file) {
    acc[fileWithoutSharedDir(file)] = filesStatus[file];
    return acc;
  }, {});
}