"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

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

function _graphlib() {
  const data = _interopRequireDefault(require("graphlib"));

  _graphlib = function () {
    return data;
  };

  return data;
}

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

  _ramda = function () {
    return data;
  };

  return data;
}

function _consumerComponent() {
  const data = _interopRequireDefault(require("../../consumer/component/consumer-component"));

  _consumerComponent = function () {
    return data;
  };

  return data;
}

function _dependencies() {
  const data = _interopRequireDefault(require("../../consumer/component/dependencies/dependencies"));

  _dependencies = function () {
    return data;
  };

  return data;
}

function _componentDependencies() {
  const data = _interopRequireDefault(require("../component-dependencies"));

  _componentDependencies = function () {
    return data;
  };

  return data;
}

function _generalError() {
  const data = _interopRequireDefault(require("../../error/general-error"));

  _generalError = function () {
    return data;
  };

  return data;
}

function _logger() {
  const data = _interopRequireDefault(require("../../logger/logger"));

  _logger = function () {
    return data;
  };

  return data;
}

const Graph = _graphlib().default.Graph;

function buildComponentsGraph(components) {
  const setGraphEdges = (component, dependencies, graph) => {
    const id = component.id.toString();
    dependencies.get().forEach(dependency => {
      const depId = dependency.id.toString(); // 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.id);
      if (!graph.hasNode(depId)) graph.setNode(depId, dependency.id);
      graph.setEdge(id, depId);
    });
  };

  const graphDeps = new Graph();
  const graphDevDeps = new Graph();
  const graphCompilerDeps = new Graph();
  const graphTesterDeps = new Graph();
  components.forEach(component => {
    setGraphEdges(component, component.dependencies, graphDeps);
    setGraphEdges(component, component.devDependencies, graphDevDeps);
    setGraphEdges(component, component.compilerDependencies, graphCompilerDeps);
    setGraphEdges(component, component.testerDependencies, graphTesterDeps);
  });
  return {
    graphDeps,
    graphDevDeps,
    graphCompilerDeps,
    graphTesterDeps
  };
}
/**
 * throw for cyclic dependencies
 * it sorts only "dependencies", not "devDependencies" (nor compiler/tester dependencies).
 */


function topologicalSortComponentDependencies(componentWithDependencies) {
  const {
    graphDeps
  } = buildComponentsGraph([componentWithDependencies.component, ...componentWithDependencies.allDependencies]);
  const componentId = componentWithDependencies.component.id.toString();
  let sortedComponents;

  try {
    sortedComponents = _graphlib().default.alg.topsort(graphDeps);
    const sortedComponentsIds = sortedComponents.map(s => graphDeps.node(s));

    const sortedDependenciesIds = _ramda().default.tail(sortedComponentsIds); // the first one is the component itself


    const dependencies = sortedDependenciesIds.map(depId => {
      const matchDependency = componentWithDependencies.dependencies.find(dependency => dependency.id.isEqual(depId));
      if (!matchDependency) throw new Error(`topologicalSortComponentDependencies, ${depId.toString()} is missing`);
      return matchDependency;
    });
    componentWithDependencies.dependencies = dependencies;
  } catch (err) {
    _logger().default.error(err);

    throw new (_generalError().default)(`unable to topological sort dependencies of ${componentId}, it probably has cyclic dependencies. Original error: ${err.message}`);
  }
}