'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 _bluebird;

function _load_bluebird() {
  return _bluebird = require('bluebird');
}

var mergeVersion = exports.mergeVersion = function () {
  var _ref = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(consumer, version, ids, mergeStrategy) {
    var getAllComponentsStatus = function () {
      var _ref4 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee() {
        var tmp, componentsStatus;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                tmp = new (_repositories || _load_repositories()).Tmp(consumer.scope);
                _context.prev = 1;
                _context.next = 4;
                return Promise.all(components.map(function (component) {
                  return getComponentStatus(consumer, component, version);
                }));

              case 4:
                componentsStatus = _context.sent;
                _context.next = 7;
                return tmp.clear();

              case 7:
                return _context.abrupt('return', componentsStatus);

              case 10:
                _context.prev = 10;
                _context.t0 = _context['catch'](1);
                _context.next = 14;
                return tmp.clear();

              case 14:
                throw _context.t0;

              case 15:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this, [[1, 10]]);
      }));

      return function getAllComponentsStatus() {
        return _ref4.apply(this, arguments);
      };
    }();

    var _ref2, components, allComponentsStatus, componentWithConflict, mergedComponentsP, mergedComponents;

    return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            _context2.next = 2;
            return consumer.loadComponents((_bitId || _load_bitId()).BitIds.fromArray(ids));

          case 2:
            _ref2 = _context2.sent;
            components = _ref2.components;
            _context2.next = 6;
            return getAllComponentsStatus();

          case 6:
            allComponentsStatus = _context2.sent;
            componentWithConflict = allComponentsStatus.find(function (component) {
              return component.mergeResults.hasConflicts;
            });

            if (!(componentWithConflict && !mergeStrategy)) {
              _context2.next = 12;
              break;
            }

            _context2.next = 11;
            return getMergeStrategyInteractive();

          case 11:
            mergeStrategy = _context2.sent;

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

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

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

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

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

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

          case 2:
            componentModel = _context3.sent;

            if (componentModel) {
              _context3.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)) {
              _context3.next = 7;
              break;
            }

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

          case 7:
            existingBitMapId = consumer.bitMap.getBitId(component.id, { ignoreVersion: true });
            currentlyUsedVersion = existingBitMapId.version;

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

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

          case 11:
            _context3.next = 13;
            return consumer.loadComponentFromModel(component.id.changeVersion(version));

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

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

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

  return function getComponentStatus(_x5, _x6, _x7) {
    return _ref5.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 _ref6 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(consumer, id, componentFromFS, mergeResults, mergeStrategy) {
    var filesStatus, component, componentMap, files, modifiedStatus, componentWriter;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
      while (1) {
        switch (_context4.prev = _context4.next) {
          case 0:
            filesStatus = {};

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

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

          case 4:
            component = componentFromFS.componentFromModel;

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

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

          case 7:
            componentMap = componentFromFS.componentMap;

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

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

          case 10:
            files = componentFromFS.files;

            component.files = files;

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

            // update files according to the merge results
            modifiedStatus = applyModifiedVersion(consumer, files, mergeResults, mergeStrategy);
            componentWriter = (_componentWriter || _load_componentWriter()).default.getInstance({
              component,
              writeToPath: component.files[0].base, // find the current path from the files. (we use the first one but it's the same for all)
              writeConfig: false, // never override the existing bit.json
              writePackageJson: false,
              deleteBitDirContent: false,
              origin: componentMap.origin,
              consumer,
              bitMap: consumer.bitMap,
              existingComponentMap: componentMap
            });
            _context4.next = 17;
            return componentWriter.write();

          case 17:

            consumer.bitMap.removeComponent(component.id);
            componentWriter.origin = componentMap.origin;
            // $FlowFixMe todo: fix this. does configDir should be a string or ConfigDir?
            componentWriter.configDir = componentMap.configDir;
            componentWriter.addComponentToBitMap(componentMap.rootDir);

            return _context4.abrupt('return', { id, filesStatus: Object.assign(filesStatus, modifiedStatus) });

          case 22:
          case 'end':
            return _context4.stop();
        }
      }
    }, _callee4, this);
  }));

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

var getMergeStrategyInteractive = exports.getMergeStrategyInteractive = function () {
  var _ref7 = (0, (_bluebird || _load_bluebird()).coroutine)( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5() {
    var result;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
      while (1) {
        switch (_context5.prev = _context5.next) {
          case 0:
            _context5.prev = 0;
            _context5.next = 3;
            return (0, (_prompts || _load_prompts()).resolveConflictPrompt)();

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

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

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

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

exports.getMergeStrategy = getMergeStrategy;

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 _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 _generalError;

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

var _componentWriter;

function _load_componentWriter() {
  return _componentWriter = _interopRequireDefault(require('../../component-ops/component-writer'));
}

var _repositories;

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

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

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 applyModifiedVersion(consumer, componentFiles, mergeResults, mergeStrategy) {
  var filesStatus = {};
  mergeResults.modifiedFiles.forEach(function (file) {
    var filePath = (_path || _load_path()).default.normalize(file.filePath);
    var foundFile = componentFiles.find(function (componentFile) {
      return componentFile.relative === filePath;
    });
    if (!foundFile) throw new (_generalError || _load_generalError()).default(`file ${filePath} not found`);
    if (mergeResults.hasConflicts && mergeStrategy === MergeOptions.theirs) {
      // write the version of otherFile
      var otherFile = file.otherFile;
      foundFile.contents = otherFile.contents;
      filesStatus[file.filePath] = FileStatus.updated;
    } else if (file.conflict) {
      foundFile.contents = Buffer.from(file.conflict);
      filesStatus[file.filePath] = FileStatus.manual;
    } else if (file.output) {
      foundFile.contents = Buffer.from(file.output);
      filesStatus[file.filePath] = FileStatus.merged;
    } else {
      throw new (_generalError || _load_generalError()).default('file does not have output nor conflict');
    }
  });
  mergeResults.addFiles.forEach(function (file) {
    var otherFile = file.otherFile;
    componentFiles.push(otherFile);
    filesStatus[file.filePath] = FileStatus.added;
  });

  return filesStatus;
}

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