'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getAutoTagPending = exports.bumpDependenciesVersions = undefined;

var _regenerator;

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

/**
 * bumping dependencies version, so-called "auto tagging" is needed when the currently tagged
 * component has dependents. these dependents should have the updated version of the currently
 * tagged component.
 *
 * to successfully accomplish the auto-tag, we do it with two rounds.
 * it's easier to understand why another round of updateComponents() is needed by an example.
 * say we have 3 components, bar/foo@0.0.1 depends on utils/is-string, utils/is-string@0.0.1 depends on
 * utils/is-type, utils/is-type@0.0.1 with no dependencies.
 * when utils/is-type is tagged, utils/is-string and bar/foo are updated in the first updateComponents() round.
 * by looking at bar/foo dependencies, we find out that its utils/is-type dependency was updated to 0.0.2
 * however, its utils/is-string dependency stays with 0.0.1, because utils/is-string was never part of
 * committedComponents array.
 * this second round of updateComponents() makes sure that the auto-tagged components will be updated as well.
 *
 * another case when round2 is needed is when the tagged component has a cycle dependency.
 * for example, A => B => C => A, and C is now tagged. the component C has the components A and B
 * in its flattenedDependencies.
 * Round1 updates A and B. It changes the C dependency to be 0.0.2 and bump their version to 0.0.2.
 * Round2 updates the dependencies and flattenedDependencies of C to have A and B with version 0.0.2.
 */
var bumpDependenciesVersions = exports.bumpDependenciesVersions = function () {
  var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(scope, potentialComponents, committedComponents) {
    var committedComponentsIds, allComponents, componentsAndVersions, graph, updatedComponents, ids;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            committedComponentsIds = (_bitId || _load_bitId()).BitIds.fromArray(committedComponents.map(function (c) {
              return c.id;
            }));
            allComponents = new (Function.prototype.bind.apply((_bitId || _load_bitId()).BitIds, [null].concat(_toConsumableArray(potentialComponents), _toConsumableArray(committedComponentsIds))))();
            _context.next = 4;
            return scope.getComponentsAndVersions(allComponents);

          case 4:
            componentsAndVersions = _context.sent;
            _context.next = 7;
            return buildGraph(scope, componentsAndVersions);

          case 7:
            graph = _context.sent;
            _context.next = 10;
            return updateComponents(componentsAndVersions, scope, committedComponentsIds, committedComponentsIds, false, graph);

          case 10:
            updatedComponents = _context.sent;

            if (!updatedComponents.length) {
              _context.next = 15;
              break;
            }

            ids = updatedComponents.map(function (component) {
              return component.toBitIdWithLatestVersion();
            });
            _context.next = 15;
            return updateComponents(componentsAndVersions, scope, committedComponentsIds, (_bitId || _load_bitId()).BitIds.fromArray(ids), true, graph);

          case 15:
            return _context.abrupt('return', updatedComponents);

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

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

var updateComponents = function () {
  var _ref2 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee3(componentsAndVersions, scope, committedComponents, changedComponents) {
    var _this = this;

    var isRound2 = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
    var graph = arguments[5];
    var componentsToUpdateP, updatedComponentsAll;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee3$(_context3) {
      while (1) {
        switch (_context3.prev = _context3.next) {
          case 0:
            componentsToUpdateP = componentsAndVersions.map(function () {
              var _ref4 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(_ref3) {
                var component = _ref3.component,
                    version = _ref3.version;
                var pendingUpdate, bitId, idStr, isCommittedComponent, edges, message;
                return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
                  while (1) {
                    switch (_context2.prev = _context2.next) {
                      case 0:
                        pendingUpdate = false;
                        bitId = component.toBitId();
                        idStr = bitId.toStringWithoutVersion();

                        if (graph.hasNode(idStr)) {
                          _context2.next = 5;
                          break;
                        }

                        return _context2.abrupt('return', null);

                      case 5:
                        isCommittedComponent = committedComponents.hasWithoutVersion(bitId);

                        if (!(isCommittedComponent && !isRound2)) {
                          _context2.next = 8;
                          break;
                        }

                        return _context2.abrupt('return', null);

                      case 8:
                        edges = (_graphlib || _load_graphlib()).default.alg.preorder(graph, idStr);

                        edges.forEach(function (edge) {
                          var edgeId = graph.node(edge);
                          var changedComponentId = changedComponents.searchWithoutVersion(edgeId);
                          if (changedComponentId && (_semver || _load_semver()).default.gt(changedComponentId.version, edgeId.version)) {
                            updateDependencies(version, edgeId, changedComponentId);
                            pendingUpdate = true;
                          }
                        });

                        if (!pendingUpdate) {
                          _context2.next = 14;
                          break;
                        }

                        message = isCommittedComponent ? version.log.message : 'bump dependencies versions';

                        if (isRound2) {
                          delete component.versions[component.latest()];
                        }
                        return _context2.abrupt('return', scope.sources.putAdditionalVersion(component, version, message));

                      case 14:
                        return _context2.abrupt('return', null);

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

              return function (_x9) {
                return _ref4.apply(this, arguments);
              };
            }());
            _context3.next = 3;
            return Promise.all(componentsToUpdateP);

          case 3:
            updatedComponentsAll = _context3.sent;
            return _context3.abrupt('return', removeNils(updatedComponentsAll));

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

  return function updateComponents(_x5, _x6, _x7, _x8) {
    return _ref2.apply(this, arguments);
  };
}();

var buildGraph = function () {
  var _ref5 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee4(scope, componentsAndVersions) {
    var graph, componentsIds;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee4$(_context4) {
      while (1) {
        switch (_context4.prev = _context4.next) {
          case 0:
            graph = new (_graphlib2 || _load_graphlib2()).Graph();
            componentsIds = (_bitId || _load_bitId()).BitIds.fromArray(componentsAndVersions.map(function (c) {
              return c.component.toBitId();
            }));

            componentsAndVersions.forEach(function (_ref6) {
              var component = _ref6.component,
                  version = _ref6.version,
                  versionStr = _ref6.versionStr;

              var id = component.id();
              version.getAllDependencies().forEach(function (dependency) {
                if (componentsIds.searchWithoutVersion(dependency.id)) {
                  var depId = dependency.id.toStringWithoutVersion();
                  // save the full BitId of a string id to be able to retrieve it later with no confusion
                  if (!graph.hasNode(id)) graph.setNode(id, component.toBitId().changeVersion(versionStr));
                  if (!graph.hasNode(depId)) graph.setNode(depId, dependency.id);
                  graph.setEdge(id, depId);
                }
              });
            });
            return _context4.abrupt('return', graph);

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

  return function buildGraph(_x10, _x11) {
    return _ref5.apply(this, arguments);
  };
}();

var getAutoTagPending = exports.getAutoTagPending = function () {
  var _ref7 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee5(scope, potentialComponents, changedComponents) {
    var componentsAndVersions, graph, autoTagPendingComponents;
    return (_regenerator || _load_regenerator()).default.wrap(function _callee5$(_context5) {
      while (1) {
        switch (_context5.prev = _context5.next) {
          case 0:
            _context5.next = 2;
            return scope.getComponentsAndVersions(new (Function.prototype.bind.apply((_bitId || _load_bitId()).BitIds, [null].concat(_toConsumableArray(potentialComponents), _toConsumableArray(changedComponents))))());

          case 2:
            componentsAndVersions = _context5.sent;
            _context5.next = 5;
            return buildGraph(scope, componentsAndVersions);

          case 5:
            graph = _context5.sent;
            autoTagPendingComponents = componentsAndVersions.map(function (_ref8) {
              var component = _ref8.component;

              var bitId = component.toBitId();
              var idStr = bitId.toStringWithoutVersion();
              if (!graph.hasNode(idStr)) return null;
              var edges = (_graphlib || _load_graphlib()).default.alg.preorder(graph, idStr);
              var isAutoTagPending = edges.some(function (edge) {
                var edgeId = graph.node(edge);
                var changedComponentId = changedComponents.searchWithoutVersion(edgeId);
                if (!changedComponentId) return false;
                if (changedComponents.searchWithoutVersion(bitId)) return false;
                // we only check whether a modified component may cause auto-tagging
                // since it's only modified on the file-system, its version might be the same as the version stored in its
                // dependents. That's why "semver.gte" is used instead of "semver.gt".
                return (_semver || _load_semver()).default.gte(changedComponentId.version, edgeId.version);
              });
              return isAutoTagPending ? component : null;
            });
            return _context5.abrupt('return', removeNils(autoTagPendingComponents));

          case 8:
          case 'end':
            return _context5.stop();
        }
      }
    }, _callee5, this);
  }));

  return function getAutoTagPending(_x12, _x13, _x14) {
    return _ref7.apply(this, arguments);
  };
}();

var _ramda;

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

var _semver;

function _load_semver() {
  return _semver = _interopRequireDefault(require('semver'));
}

var _graphlib;

function _load_graphlib() {
  return _graphlib = _interopRequireDefault(require('graphlib'));
}

var _graphlib2;

function _load_graphlib2() {
  return _graphlib2 = require('graphlib');
}

var _component;

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

var _bitId;

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

var _scope;

function _load_scope() {
  return _scope = _interopRequireDefault(require('../scope'));
}

var _dependencies;

function _load_dependencies() {
  return _dependencies = require('../../consumer/component/dependencies');
}

var _component2;

function _load_component2() {
  return _component2 = _interopRequireDefault(require('../../consumer/component'));
}

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

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

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 removeNils = (_ramda || _load_ramda()).default.reject((_ramda || _load_ramda()).default.isNil);

function updateDependencies(version, edgeId, changedComponentId) {
  version.updateFlattenedDependency(edgeId, edgeId.changeVersion(changedComponentId.version));
  var dependencyToUpdate = version.getAllDependencies().find(function (dependency) {
    return dependency.id.isEqualWithoutVersion(edgeId);
  });
  if (dependencyToUpdate) {
    // it's a direct dependency
    dependencyToUpdate.id = dependencyToUpdate.id.changeVersion(changedComponentId.version);
  }
}