"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 _ramda() {
  const data = require("ramda");

  _ramda = function () {
    return data;
  };

  return data;
}

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

  _bitId = function () {
    return data;
  };

  return data;
}

function _remote() {
  const data = _interopRequireDefault(require("./remote"));

  _remote = function () {
    return data;
  };

  return data;
}

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

  _utils = function () {
    return data;
  };

  return data;
}

function _exceptions() {
  const data = require("./exceptions");

  _exceptions = function () {
    return data;
  };

  return data;
}

function _componentObjects() {
  const data = _interopRequireDefault(require("../scope/component-objects"));

  _componentObjects = function () {
    return data;
  };

  return data;
}

function _remoteResolver() {
  const data = _interopRequireDefault(require("./remote-resolver/remote-resolver"));

  _remoteResolver = function () {
    return data;
  };

  return data;
}

function _globalRemotes() {
  const data = _interopRequireDefault(require("../global-config/global-remotes"));

  _globalRemotes = function () {
    return data;
  };

  return data;
}

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

  _logger = function () {
    return data;
  };

  return data;
}

class Remotes extends Map {
  constructor(remotes = []) {
    super(remotes);
  }

  validate() {
    const primary = this.values.filter(remote => remote.primary);
    if (primary.length > 1) throw new (_exceptions().PrimaryOverloaded)();
    return this.forEach(remote => remote.validate());
  }

  resolve(scopeName, thisScope) {
    const remote = super.get(scopeName);
    if (remote) return Promise.resolve(remote);
    return (0, _remoteResolver().default)(scopeName, thisScope).then(scopeHost => {
      return new (_remote().default)(scopeHost, scopeName);
    });
  }

  isHub(scope) {
    // if a scope is listed as a remote, it doesn't go to the hub
    return !this.get(scope);
  }

  fetch(ids, thisScope, withoutDeps = false, context) {
    var _this = this;

    return (0, _bluebird().coroutine)(function* () {
      // TODO - Transfer the fetch logic into the ssh module,
      // in order to close the ssh connection in the end of the multifetch instead of one fetch
      const byScope = (0, _ramda().groupBy)((0, _ramda().prop)('scope'));
      const promises = [];
      (0, _utils().forEach)(byScope(ids), (scopeIds, scopeName) => {
        promises.push(_this.resolve(scopeName, thisScope).then(remote => remote.fetch(_bitId().BitIds.fromArray(scopeIds), withoutDeps, context)));
      });

      _logger().default.debug(`[-] Running fetch (withoutDeps: ${withoutDeps.toString()}) on a remote`);

      const bits = yield Promise.all(promises);

      _logger().default.debug('[-] Returning from a remote');

      return (0, _utils().flatten)(bits);
    })();
  }

  latestVersions(ids, thisScope) {
    var _this2 = this;

    return (0, _bluebird().coroutine)(function* () {
      const byScope = (0, _ramda().groupBy)((0, _ramda().prop)('scope'));
      const promises = [];
      (0, _utils().forEach)(byScope(ids), (scopeIds, scopeName) => {
        promises.push(_this2.resolve(scopeName, thisScope).then(remote => remote.latestVersions(scopeIds)));
      });
      const components = yield Promise.all(promises);
      const flattenComponents = (0, _utils().flatten)(components);
      return flattenComponents.map(componentId => _bitId().BitId.parse(componentId, true));
    })();
  }

  toPlainObject() {
    const object = {};
    this.forEach(remote => {
      let name = remote.name;
      if (remote.primary) name = (0, _utils().prependBang)(remote.name);
      object[name] = remote.host;
    });
    return object;
  }

  static getScopeRemote(scopeName) {
    return Remotes.getGlobalRemotes().then(remotes => remotes.resolve(scopeName));
  }

  static getGlobalRemotes() {
    return _globalRemotes().default.load().then(globalRemotes => globalRemotes.toPlainObject()).then(remotes => Remotes.load(remotes));
  }

  static load(remotes) {
    const models = [];
    if (!remotes) return new Remotes();
    (0, _utils().forEach)(remotes, (name, host) => {
      const remote = _remote().default.load(name, host);

      models.push([remote.name, remote]);
    });
    return new Remotes(models);
  }

}

exports.default = Remotes;