"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 _graphlib() {
  const data = _interopRequireDefault(require("graphlib"));

  _graphlib = function () {
    return data;
  };

  return data;
}

function _semver() {
  const data = _interopRequireDefault(require("semver"));

  _semver = function () {
    return data;
  };

  return data;
}

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

  _ramda = function () {
    return data;
  };

  return data;
}

function _bitId() {
  const data = require("../../bit-id");

  _bitId = function () {
    return data;
  };

  return data;
}

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

  _constants = function () {
    return data;
  };

  return data;
}

function _scope() {
  const data = _interopRequireDefault(require("../scope"));

  _scope = function () {
    return data;
  };

  return data;
}

const Graph = _graphlib().default.Graph;

class DependencyGraph {
  constructor(scope, graph) {
    this.scope = scope;
    this.graph = graph;
  }

  static load(scope) {
    return (0, _bluebird().coroutine)(function* () {
      const graph = yield DependencyGraph.buildDependenciesGraph(scope);
      return new DependencyGraph(scope, graph);
    })();
  }

  static buildDependenciesGraph(scope) {
    return (0, _bluebird().coroutine)(function* () {
      const graph = new Graph({
        compound: true
      });
      const depObj = {};
      const allComponents = yield scope.list(); // build all nodes. a node is either a Version object or Component object.
      // each Version node has a parent of Component node. Component node doesn't have a parent.

      yield Promise.all(allComponents.map(
      /*#__PURE__*/
      function () {
        var _ref = (0, _bluebird().coroutine)(function* (component) {
          graph.setNode(component.id(), component);
          yield Promise.all(Object.keys(component.versions).map(
          /*#__PURE__*/
          function () {
            var _ref2 = (0, _bluebird().coroutine)(function* (version) {
              const componentVersion = yield component.loadVersion(version, scope.objects);
              if (!componentVersion) return;
              const idWithVersion = `${component.id()}${_constants().VERSION_DELIMITER}${version}`;
              graph.setNode(idWithVersion, componentVersion);
              graph.setParent(idWithVersion, component.id());
              componentVersion.id = component.toBitId();
              depObj[idWithVersion] = componentVersion;
            });

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

        return function (_x) {
          return _ref.apply(this, arguments);
        };
      }())); // set all edges
      // @todo: currently the label is "require". Change it to be "direct" and "indirect" depends on whether it comes from
      // flattenedDependencies or from dependencies.

      Object.keys(depObj).forEach(id => depObj[id].flattenedDependencies.forEach(dep => graph.setEdge(id, dep.toString(), 'require')));
      return Promise.resolve(graph);
    })();
  }

  getComponent(id) {
    return this.graph.node(id.toStringWithoutVersion());
  }

  getComponentVersion(id) {
    if (id.version === _constants().LATEST_BIT_VERSION) {
      const component = this.getComponent();
      const versions = Object.keys(component.versions);

      const latestVersion = _semver().default.maxSatisfying(versions, '*');

      return this.graph.node(`${id.toStringWithoutVersion()}${_constants().VERSION_DELIMITER}${latestVersion}`);
    }

    return this.graph.node(id.toString());
  }

  getDependentsPerId(id) {
    const nodeEdges = this.graph.inEdges(id.toString());
    if (!nodeEdges) return [];
    return nodeEdges.map(node => node.v);
  }

  getDependenciesPerId(id) {
    const nodeEdges = this.graph.outEdges(id.toString());
    if (!nodeEdges) return [];
    return nodeEdges.map(node => node.v);
  }

  getComponentVersions(id) {
    const component = this.getComponent(id);
    if (!component) return;
    return Object.keys(component.versions).map(version => this.getComponentVersion(`${id.toStringWithoutVersion()}${_constants().VERSION_DELIMITER}${version}`)).filter(x => x);
  }
  /**
   * findDependentBits
   * foreach component in array find the componnet that uses that component
   * dont return local components
   */


  findDependentBitsByGraph(bitIds) {
    var _this = this;

    return (0, _bluebird().coroutine)(function* () {
      const dependentIds = {};
      bitIds.forEach(id => {
        if (id.version === _constants().LATEST_BIT_VERSION) {
          // check if another component in the scope is using any of the versions
          const children = _this.graph.children(id.toString());

          children.forEach(child => {
            const requiredBy = _this.graph.predecessors(child);

            if (requiredBy && !_ramda().default.isEmpty(requiredBy)) {
              if (dependentIds[id.toStringWithoutVersion()]) {
                dependentIds[id.toStringWithoutVersion()] = dependentIds[id.toStringWithoutVersion()].concat(requiredBy);
              } else dependentIds[id.toStringWithoutVersion()] = requiredBy;
            }
          });
        } else {
          const requiredBy = _this.graph.predecessors(id.toString());

          if (!_ramda().default.isEmpty(requiredBy)) {
            if (dependentIds[id.toString()]) dependentIds[id.toString()] = dependentIds[id.toString()].concat(requiredBy);else dependentIds[id.toString()] = requiredBy;
          }
        }
      });
      return dependentIds;
    })();
  }

}

exports.default = DependencyGraph;