"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 _defineProperty2() {
  const data = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

  _defineProperty2 = function () {
    return data;
  };

  return data;
}

function _path() {
  const data = _interopRequireDefault(require("path"));

  _path = 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 _modelComponent() {
  const data = _interopRequireDefault(require("../../scope/models/model-component"));

  _modelComponent = function () {
    return data;
  };

  return data;
}

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

  _scope = function () {
    return data;
  };

  return data;
}

function _component() {
  const data = _interopRequireDefault(require("../component"));

  _component = function () {
    return data;
  };

  return data;
}

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

  _bitId = function () {
    return data;
  };

  return data;
}

function _bitMap() {
  const data = _interopRequireDefault(require("../bit-map/bit-map"));

  _bitMap = function () {
    return data;
  };

  return data;
}

function _utils() {
  const data = require("../../utils");

  _utils = function () {
    return data;
  };

  return data;
}

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

  _constants = function () {
    return data;
  };

  return data;
}

function _noIdMatchWildcard() {
  const data = _interopRequireDefault(require("../../api/consumer/lib/exceptions/no-id-match-wildcard"));

  _noIdMatchWildcard = function () {
    return data;
  };

  return data;
}

function _scopeRemotes() {
  const data = require("../../scope/scope-remotes");

  _scopeRemotes = function () {
    return data;
  };

  return data;
}

function _isBitIdMatchByWildcards() {
  const data = _interopRequireDefault(require("../../utils/bit/is-bit-id-match-by-wildcards"));

  _isBitIdMatchByWildcards = function () {
    return data;
  };

  return data;
}

class ComponentsList {
  constructor(consumer) {
    (0, _defineProperty2().default)(this, "_fromFileSystem", {});
    this.consumer = consumer;
    this.scope = consumer.scope;
    this.bitMap = consumer.bitMap;
  }

  getModelComponents() {
    var _this = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!_this._modelComponents) {
        _this._modelComponents = yield _this.scope.list();
      }

      return _this._modelComponents;
    })();
  }
  /**
   * List all bit ids stored in the model
   */


  getFromObjects() {
    var _this2 = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!_this2._fromObjectsIds) {
        const modelComponents = yield _this2.getModelComponents();
        _this2._fromObjectsIds = modelComponents.map(componentObjects => {
          return new (_bitId().BitId)({
            scope: componentObjects.scope,
            name: componentObjects.name,
            version: componentObjects.scope ? componentObjects.latest() : null
          });
        });
      }

      return _this2._fromObjectsIds;
    })();
  }

  _getAuthoredAndImportedFromFS() {
    var _this3 = this;

    return (0, _bluebird().coroutine)(function* () {
      let [authored, imported] = yield Promise.all([_this3.getFromFileSystem(_constants().COMPONENT_ORIGINS.AUTHORED), _this3.getFromFileSystem(_constants().COMPONENT_ORIGINS.IMPORTED)]);
      authored = authored || [];
      imported = imported || [];
      return authored.concat(imported);
    })();
  }
  /**
   * Components that are in the model (either, tagged from a local scope or imported), and were
   * changed in the file system
   *
   * @param {boolean} [load=false] - Whether to load the component (false will return only the id)
   * @return {Promise<string[]>}
   */


  listModifiedComponents(load = false) {
    var _this4 = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!_this4._modifiedComponents) {
        const fileSystemComponents = yield _this4._getAuthoredAndImportedFromFS();
        _this4._modifiedComponents = yield (0, _utils().filterAsync)(fileSystemComponents, component => {
          return _this4.consumer.getComponentStatusById(component.id).then(status => status.modified);
        });
      }

      if (load) return _this4._modifiedComponents;
      return _this4._modifiedComponents.map(component => component.id);
    })();
  }

  listOutdatedComponents() {
    var _this5 = this;

    return (0, _bluebird().coroutine)(function* () {
      const fileSystemComponents = yield _this5._getAuthoredAndImportedFromFS();
      const componentsFromModel = yield _this5.getModelComponents();
      return fileSystemComponents.filter(component => {
        const modelComponent = componentsFromModel.find(c => c.toBitId().isEqualWithoutVersion(component.id));
        if (!modelComponent) return false;
        const latestVersion = modelComponent.latest();

        if (component.id.hasVersion() && _semver().default.gt(latestVersion, component.id.version)) {
          component.latestVersion = latestVersion;
          return true;
        }

        return false;
      });
    })();
  }

  newModifiedAndAutoTaggedComponents() {
    var _this6 = this;

    return (0, _bluebird().coroutine)(function* () {
      const [newComponents, modifiedComponents] = yield Promise.all([_this6.listNewComponents(true), _this6.listModifiedComponents(true)]);
      const autoTagPendingModel = yield _this6.listAutoTagPendingComponents();
      const autoTagPending = yield _this6.scope.toConsumerComponents(autoTagPendingModel);
      const components = [...newComponents, ...modifiedComponents, ...autoTagPending];
      return Promise.all(components);
    })();
  }

  authoredAndImportedComponents() {
    var _this7 = this;

    return (0, _bluebird().coroutine)(function* () {
      return _this7._getAuthoredAndImportedFromFS();
    })();
  }

  idsFromObjects() {
    var _this8 = this;

    return (0, _bluebird().coroutine)(function* () {
      const fromObjects = yield _this8.getFromObjects();
      return new (_bitId().BitIds)(...fromObjects);
    })();
  }
  /**
   * Components that are registered in bit.map but have never been tagged
   *
   * @param {boolean} [load=false] - Whether to load the component (false will return only the id)
   * @return {Promise.<string[] | Component[]>}
   * @memberof ComponentsList
   */


  listNewComponents(load = false) {
    var _this9 = this;

    return (0, _bluebird().coroutine)(function* () {
      const idsFromBitMap = _this9.idsFromBitMap();

      const idsFromObjects = yield _this9.idsFromObjects();
      const newComponents = [];
      idsFromBitMap.forEach(id => {
        if (!idsFromObjects.searchWithoutScopeAndVersion(id)) {
          newComponents.push(id);
        }
      });
      const newComponentsIds = new (_bitId().BitIds)(...newComponents);
      if (!load || !newComponents.length) return newComponentsIds;
      const {
        components
      } = yield _this9.consumer.loadComponents(newComponentsIds, false);
      return components;
    })();
  }
  /**
   * Authored exported components (easily identified by having a scope) which are not saved in the model are
   * import-pending. Exclude them from the 'newComponents' and add them to 'importPendingComponents'.
   */


  listNewComponentsAndImportPending() {
    var _this10 = this;

    return (0, _bluebird().coroutine)(function* () {
      const allNewComponents = yield _this10.listNewComponents(true);
      const newComponents = [];
      const importPendingComponents = [];
      allNewComponents.forEach(component => {
        if (component.id.scope) {
          importPendingComponents.push(component);
        } else {
          newComponents.push(component);
        }
      });
      return {
        newComponents,
        importPendingComponents
      };
    })();
  }

  listCommitPendingOfAllScope(version, includeImported = false) {
    var _this11 = this;

    return (0, _bluebird().coroutine)(function* () {
      let tagPendingComponents;
      tagPendingComponents = _this11.idsFromBitMap(_constants().COMPONENT_ORIGINS.AUTHORED);

      if (includeImported) {
        const importedComponents = _this11.idsFromBitMap(_constants().COMPONENT_ORIGINS.IMPORTED);

        tagPendingComponents = tagPendingComponents.concat(importedComponents);
      }

      const tagPendingComponentsLatest = yield _this11.scope.latestVersions(tagPendingComponents, false);
      const warnings = [];
      tagPendingComponentsLatest.forEach(componentId => {
        if (_semver().default.gt(componentId.version, version)) {
          warnings.push(`warning: ${componentId.toString()} has a version greater than ${version}`);
        }
      });
      return {
        tagPendingComponents,
        warnings
      };
    })();
  }
  /**
   * New and modified components are tag pending
   *
   * @return {Promise<string[]>}
   */


  listCommitPendingComponents() {
    var _this12 = this;

    return (0, _bluebird().coroutine)(function* () {
      const [newComponents, modifiedComponents] = yield Promise.all([_this12.listNewComponents(), _this12.listModifiedComponents()]);
      return _bitId().BitIds.fromArray([...newComponents, ...modifiedComponents]);
    })();
  }

  listExportPendingComponentsIds() {
    var _this13 = this;

    return (0, _bluebird().coroutine)(function* () {
      const modelComponents = yield _this13.getModelComponents();
      const pendingExportComponents = modelComponents.filter(component => component.isLocallyChanged());

      const ids = _bitId().BitIds.fromArray(pendingExportComponents.map(c => c.toBitId()));

      return _this13.updateIdsFromModelIfTheyOutOfSync(ids);
    })();
  }

  updateIdsFromModelIfTheyOutOfSync(ids) {
    var _this14 = this;

    return (0, _bluebird().coroutine)(function* () {
      const authoredAndImported = _this14.bitMap.getAuthoredAndImportedBitIds();

      const updatedIdsP = ids.map(
      /*#__PURE__*/
      function () {
        var _ref = (0, _bluebird().coroutine)(function* (id) {
          const idFromBitMap = authoredAndImported.searchWithoutScopeAndVersion(id);

          if (idFromBitMap && !idFromBitMap.hasVersion()) {
            // component is out of sync, fix it by loading it from the consumer
            const component = yield _this14.consumer.loadComponent(id.changeVersion(_constants().LATEST));
            return component.id;
          }

          return id;
        });

        return function (_x) {
          return _ref.apply(this, arguments);
        };
      }());
      const updatedIds = yield Promise.all(updatedIdsP);
      return _bitId().BitIds.fromArray(updatedIds);
    })();
  }

  listExportPendingComponents() {
    var _this15 = this;

    return (0, _bluebird().coroutine)(function* () {
      const exportPendingComponentsIds = yield _this15.listExportPendingComponentsIds(); // $FlowFixMe

      return Promise.all(exportPendingComponentsIds.map(id => _this15.scope.getModelComponentIfExist(id)));
    })();
  }

  listAutoTagPendingComponents() {
    var _this16 = this;

    return (0, _bluebird().coroutine)(function* () {
      const modifiedComponents = yield _this16.listModifiedComponents();
      if (!modifiedComponents || !modifiedComponents.length) return [];
      const modifiedComponentsLatestVersions = yield _this16.scope.latestVersions(modifiedComponents);
      return _this16.consumer.listComponentsForAutoTagging(modifiedComponentsLatestVersions);
    })();
  }

  idsFromBitMap(origin) {
    const fromBitMap = this.getFromBitMap(origin);
    return fromBitMap;
  }
  /**
   * Finds all components that are saved in the file system.
   * Components might be stored in the default component directory and also might be outside
   * of that directory. The bit.map is used to find them all
   * If they are on bit.map but not on the file-system, populate them to _invalidComponents property
   */


  getFromFileSystem(origin) {
    var _this17 = this;

    return (0, _bluebird().coroutine)(function* () {
      const cacheKeyName = origin || 'all';

      if (!_this17._fromFileSystem[cacheKeyName]) {
        const idsFromBitMap = _this17.idsFromBitMap(origin);

        const {
          components,
          invalidComponents
        } = yield _this17.consumer.loadComponents(idsFromBitMap, false);
        _this17._fromFileSystem[cacheKeyName] = components;

        if (!_this17._invalidComponents && !origin) {
          _this17._invalidComponents = invalidComponents;
        }
      }

      return _this17._fromFileSystem[cacheKeyName];
    })();
  }
  /**
   * components that are on bit.map but not on the file-system
   */


  listInvalidComponents() {
    var _this18 = this;

    return (0, _bluebird().coroutine)(function* () {
      if (!_this18._invalidComponents) {
        yield _this18.getFromFileSystem();
      }

      return _this18._invalidComponents;
    })();
  }

  getFromBitMap(origin) {
    const originParam = origin ? [origin] : undefined;
    return this.bitMap.getAllBitIds(originParam);
  }

  getPathsForAllFilesOfAllComponents(origin, absolute = false) {
    // TODO: maybe cache this as well
    const componentsFromBitMap = this.bitMap.getAllComponents(origin);
    const res = [];

    const getPaths = (agg, isAbsolute) => (componentMap, componentId) => {
      const relativePaths = componentMap.getFilesRelativeToConsumer();

      if (!isAbsolute) {
        agg.push(...relativePaths);
        return;
      }

      const consumerPath = this.consumer.getPath();
      const absPaths = relativePaths.map(relativePath => _path().default.join(consumerPath, relativePath));
      agg.push(...absPaths);
    };

    _ramda().default.forEachObjIndexed(getPaths(res, absolute), componentsFromBitMap);

    return res;
  }
  /**
   * get called when the Consumer is available, shows also components from remote scopes
   */


  listScope(showRemoteVersion, includeNested, namespacesUsingWildcards) {
    var _this19 = this;

    return (0, _bluebird().coroutine)(function* () {
      const components = yield _this19.getModelComponents();
      const componentsFilteredByWildcards = namespacesUsingWildcards ? ComponentsList.filterComponentsByWildcard(components, namespacesUsingWildcards) : components;
      const componentsSorted = ComponentsList.sortComponentsByName(componentsFilteredByWildcards);
      const listScopeResults = componentsSorted.map(component => ({
        id: component.toBitIdWithLatestVersion(),
        deprecated: component.deprecated
      }));
      const componentsIds = listScopeResults.map(result => result.id);

      if (showRemoteVersion) {
        const latestVersionsInfo = yield (0, _scopeRemotes().fetchRemoteVersions)(_this19.scope, componentsIds);
        latestVersionsInfo.forEach(componentId => {
          const listResult = listScopeResults.find(c => c.id.isEqualWithoutVersion(componentId));
          if (!listResult) throw new Error(`failed finding ${componentId.toString()} in componentsIds`); // $FlowFixMe version must be set as it came from a remote

          listResult.remoteVersion = componentId.version;
        });
      }

      const authoredAndImportedIds = _this19.bitMap.getAuthoredAndImportedBitIds();

      listScopeResults.forEach(listResult => {
        const existingBitMapId = authoredAndImportedIds.searchWithoutVersion(listResult.id);

        if (existingBitMapId) {
          listResult.currentlyUsedVersion = existingBitMapId.version;
        }
      });
      if (includeNested) return listScopeResults;
      return listScopeResults.filter(listResult => {
        const componentMap = _this19.bitMap.getComponentIfExist(listResult.id, {
          ignoreVersion: true
        });

        return componentMap && componentMap.origin !== _constants().COMPONENT_ORIGINS.NESTED;
      });
    })();
  }
  /**
   * get called from a bare-scope, shows only components of that scope
   */


  static listLocalScope(scope, namespacesUsingWildcards) {
    return (0, _bluebird().coroutine)(function* () {
      const components = yield scope.listLocal();
      const componentsFilteredByWildcards = namespacesUsingWildcards ? ComponentsList.filterComponentsByWildcard(components, namespacesUsingWildcards) : components;
      const componentsSorted = ComponentsList.sortComponentsByName(componentsFilteredByWildcards);
      return componentsSorted.map(component => ({
        id: component.toBitIdWithLatestVersion(),
        deprecated: component.deprecated
      }));
    })();
  } // components can be one of the following: Component[] | ModelComponent[] | string[]


  static sortComponentsByName(components) {
    const getName = component => {
      let name;
      if (_ramda().default.is(_modelComponent().default, component)) name = component.id();else if (_ramda().default.is(_component().default, component)) name = component.id.toString();else name = component;
      return name.toUpperCase(); // ignore upper and lowercase
    }; // $FlowFixMe


    return components.sort((a, b) => {
      const nameA = getName(a);
      const nameB = getName(b);

      if (nameA < nameB) {
        return -1;
      }

      if (nameA > nameB) {
        return 1;
      } // names must be equal


      return 0;
    });
  }

  static filterComponentsByWildcard(components, idsWithWildcard) {
    const getBitId = component => {
      if (_ramda().default.is(_modelComponent().default, component)) return component.toBitId();
      if (_ramda().default.is(_component().default, component)) return component.id;
      if (_ramda().default.is(_bitId().BitId, component)) return component;
      throw new TypeError(`filterComponentsByWildcard got component with the wrong type: ${typeof component}`);
    }; // $FlowFixMe


    return components.filter(component => {
      const bitId = getBitId(component);
      return (0, _isBitIdMatchByWildcards().default)(bitId, idsWithWildcard);
    });
  }

  static getUniqueComponents(components) {
    return _ramda().default.uniqBy(component => JSON.stringify(component.id), components);
  }

  listComponentsByIdsWithWildcard(idsWithWildcard) {
    const allIds = this.bitMap.getAuthoredAndImportedBitIds();
    const matchedIds = ComponentsList.filterComponentsByWildcard(allIds, idsWithWildcard);
    if (!matchedIds.length) throw new (_noIdMatchWildcard().default)(idsWithWildcard); // $FlowFixMe filterComponentsByWildcard got BitId so it returns BitId

    return matchedIds;
  }

}

exports.default = ComponentsList;