'use strict';

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

var _path;

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

var _ramda;

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

var _semver;

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

var _bitId;

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

var _logger;

function _load_logger() {
  return _logger = _interopRequireDefault(require('../../../../logger/logger'));
}

var _componentNodeModulesPath;

function _load_componentNodeModulesPath() {
  return _componentNodeModulesPath = _interopRequireDefault(require('../../../../utils/bit/component-node-modules-path'));
}

var _dependencies;

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

var _componentIdToPackageName;

function _load_componentIdToPackageName() {
  return _componentIdToPackageName = _interopRequireDefault(require('../../../../utils/bit/component-id-to-package-name'));
}

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

/**
 * The dependency version is determined by the following strategies by this order.
 * 1) if the component bit.json or package.json has "overrides" property, check whether the dependency version is overridden
 * 2) if package.json is different than the model, use package.json. to find the package.json follow this steps:
 * 2 a) search in the component directory for package.json and look for dependencies or devDependencies with the name of the dependency
 * 2 b) if not found there, propagate until you reach the consumer root directory.
 * 2 c) if not found, go directly to the dependency directory and find the version in its package.json
 * 3) if workspace-config overrides this component, find whether it overrides the dependency version and use it.
 * 4) if bitmap has a version, use it.
 * 5) use the model if it has a version.
 * 6) use the package.json regardless the model.
 *
 * cases where dependency version may be different than the model:
 * 1) user added the component to `overrides` of the workspace config. (workspace bit.json or package.json)
 * 2) user changed package.json, either, manually or by npm-install —save.
 * 3) user updated a dependency with npm without —save.
 * 4) user imported the dependency with different version causing the bitmap to change.
 *
 * keep in mind that since v14.0.5 bit.json doesn't have the dependencies, so it's impossible
 * to change a dependency version from the component bit.json.
 */
function updateDependenciesVersions(consumer, component) {
  updateDependencies(component.dependencies);
  updateDependencies(component.devDependencies);
  updateDependencies(component.compilerDependencies);
  updateDependencies(component.testerDependencies);

  function updateDependencies(dependencies) {
    dependencies.get().forEach(function (dependency) {
      var id = dependency.id;
      // $FlowFixMe component.componentFromModel is set
      var idFromModel = getIdFromModelDeps(component.componentFromModel, id);
      var idFromPackageJson = getIdFromPackageJson(id);
      var idFromBitMap = getIdFromBitMap(id);
      var idFromWorkspaceConfig = getIdFromWorkspaceConfig(id);
      var idFromComponentConfig = getIdFromComponentConfig(id);
      var idFromDependentPackageJson = getIdFromDependentPackageJson(id);

      // get from packageJson when it was changed from the model or when there is no model.
      var getFromPackageJsonIfChanged = function getFromPackageJsonIfChanged() {
        if (!idFromPackageJson) return null;
        if (!idFromModel) return idFromPackageJson;
        if (!idFromPackageJson.isEqual(idFromModel)) return idFromPackageJson;
        return null;
      };
      var getFromWorkspaceConfig = function getFromWorkspaceConfig() {
        return idFromWorkspaceConfig || null;
      };
      var getFromComponentConfig = function getFromComponentConfig() {
        return idFromComponentConfig || null;
      };
      var getFromBitMap = function getFromBitMap() {
        return idFromBitMap || null;
      };
      var getFromModel = function getFromModel() {
        return idFromModel || null;
      };
      var getFromPackageJson = function getFromPackageJson() {
        return idFromPackageJson || null;
      };
      var getFromDependentPackageJson = function getFromDependentPackageJson() {
        return idFromDependentPackageJson || null;
      };
      var strategies = [getFromComponentConfig, getFromDependentPackageJson, getFromPackageJsonIfChanged, getFromWorkspaceConfig, getFromBitMap, getFromModel, getFromPackageJson];

      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = strategies[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var strategy = _step.value;

          var strategyId = strategy();
          if (strategyId) {
            dependency.id = dependency.id.changeVersion(strategyId.version);
            (_logger || _load_logger()).default.debug(`found dependency version ${dependency.id.toString()} in strategy ${strategy.name}`);
            return;
          }
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }
    });
  }
  function getIdFromModelDeps(componentFromModel, componentId) {
    if (!componentFromModel) return null;
    var dependency = componentFromModel.getAllDependenciesIds().searchWithoutVersion(componentId);
    if (!dependency) return null;
    return dependency;
  }

  /**
   * the logic of finding the dependency version in the package.json is mostly done in the driver
   * resolveNodePackage method.
   * it first searches in the dependent package.json and propagate up to the consumer root, if not
   * found it goes to the dependency package.json.
   */
  function getIdFromPackageJson(componentId) {
    if (!componentId.scope) return null;
    // $FlowFixMe component.componentMap is set
    var rootDir = component.componentMap.rootDir;
    var consumerPath = consumer.getPath();
    var basePath = rootDir ? (_path || _load_path()).default.join(consumerPath, rootDir) : consumerPath;
    var packagePath = (0, (_componentNodeModulesPath || _load_componentNodeModulesPath()).default)(component.bindingPrefix, componentId);
    var packageName = packagePath.replace(`node_modules${(_path || _load_path()).default.sep}`, '');
    var modulePath = consumer.driver.driver.resolveModulePath(packageName, basePath, consumerPath);
    if (!modulePath) return null; // e.g. it's author and wasn't exported yet, so there's no node_modules of that component
    var packageObject = consumer.driver.driver.resolveNodePackage(basePath, modulePath);
    if (!packageObject || (_ramda || _load_ramda()).default.isEmpty(packageObject)) return null;
    var packageId = Object.keys(packageObject)[0];
    var version = packageObject[packageId];
    if (!(_semver || _load_semver()).default.valid(version) && !(_semver || _load_semver()).default.validRange(version)) return null; // it's probably a relative path to the component
    var validVersion = version.replace(/[^0-9.]/g, ''); // allow only numbers and dots to get an exact version
    return componentId.changeVersion(validVersion);
  }

  function getIdFromDependentPackageJson(componentId) {
    // for author, there is not package.json of a component
    if (!component.bitJson || !component.bitJson.packageJsonObject || !component.bitJson.packageJsonObject.dependencies) {
      return null;
    }
    var dependencyIdAsPackage = (0, (_componentIdToPackageName || _load_componentIdToPackageName()).default)(componentId, component.bindingPrefix);
    // $FlowFixMe
    var version = component.bitJson.packageJsonObject.dependencies[dependencyIdAsPackage];
    if (!(_semver || _load_semver()).default.valid(version) && !(_semver || _load_semver()).default.validRange(version)) return null; // it's probably a relative path to the component
    var validVersion = version.replace(/[^0-9.]/g, ''); // allow only numbers and dots to get an exact version
    return componentId.changeVersion(validVersion);
  }

  function getIdFromBitMap(componentId) {
    return consumer.bitMap.getBitIdIfExist(componentId, { ignoreVersion: true });
  }

  function getIdFromWorkspaceConfig(componentId) {
    var dependencies = component.overrides.getComponentDependenciesWithVersionFromConsumer();
    if ((_ramda || _load_ramda()).default.isEmpty(dependencies)) return null;
    var dependency = Object.keys(dependencies).find(function (idStr) {
      return componentId.toStringWithoutVersion() === idStr || componentId.toStringWithoutScopeAndVersion() === idStr;
    });
    if (!dependency) return null;
    return componentId.changeVersion(dependencies[dependency]);
  }

  function getIdFromComponentConfig(componentId) {
    var dependencies = component.overrides.getComponentDependenciesWithVersion();
    if ((_ramda || _load_ramda()).default.isEmpty(dependencies)) return null;
    var dependency = Object.keys(dependencies).find(function (idStr) {
      return componentId.toStringWithoutVersion() === idStr || componentId.toStringWithoutScopeAndVersion() === idStr;
    });
    if (!dependency) return null;
    return componentId.changeVersion(dependencies[dependency]);
  }
}