'use strict';

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

var _regenerator;

function _load_regenerator() {
  return _regenerator = _interopRequireDefault(require('babel-runtime/regenerator'));
}

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _ramda;

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

var _bitId;

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

var _remote;

function _load_remote() {
  return _remote = _interopRequireDefault(require('./remote'));
}

var _utils;

function _load_utils() {
  return _utils = require('../utils');
}

var _exceptions;

function _load_exceptions() {
  return _exceptions = require('./exceptions');
}

var _componentObjects;

function _load_componentObjects() {
  return _componentObjects = _interopRequireDefault(require('../scope/component-objects'));
}

var _remoteResolver;

function _load_remoteResolver() {
  return _remoteResolver = _interopRequireDefault(require('./remote-resolver/remote-resolver'));
}

var _globalConfig;

function _load_globalConfig() {
  return _globalConfig = require('../global-config');
}

var _scope;

function _load_scope() {
  return _scope = _interopRequireDefault(require('../scope/scope'));
}

var _logger;

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

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

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _extendableBuiltin(cls) {
  function ExtendableBuiltin() {
    var instance = Reflect.construct(cls, Array.from(arguments));
    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    return instance;
  }

  ExtendableBuiltin.prototype = Object.create(cls.prototype, {
    constructor: {
      value: cls,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });

  if (Object.setPrototypeOf) {
    Object.setPrototypeOf(ExtendableBuiltin, cls);
  } else {
    ExtendableBuiltin.__proto__ = cls;
  }

  return ExtendableBuiltin;
}

var Remotes = function (_extendableBuiltin2) {
  _inherits(Remotes, _extendableBuiltin2);

  function Remotes() {
    var remotes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];

    _classCallCheck(this, Remotes);

    return _possibleConstructorReturn(this, (Remotes.__proto__ || Object.getPrototypeOf(Remotes)).call(this, remotes));
  }

  _createClass(Remotes, [{
    key: 'validate',
    value: function validate() {
      var primary = this.values.filter(function (remote) {
        return remote.primary;
      });
      if (primary.length > 1) throw new (_exceptions || _load_exceptions()).PrimaryOverloaded();
      return this.forEach(function (remote) {
        return remote.validate();
      });
    }
  }, {
    key: 'resolve',
    value: function resolve(scopeName, thisScope) {
      var remote = _get(Remotes.prototype.__proto__ || Object.getPrototypeOf(Remotes.prototype), 'get', this).call(this, scopeName);
      if (remote) return Promise.resolve(remote);
      return (0, (_remoteResolver || _load_remoteResolver()).default)(scopeName, thisScope).then(function (scopeHost) {
        return new (_remote || _load_remote()).default(scopeHost, scopeName);
      });
    }
  }, {
    key: 'isHub',
    value: function isHub(scope) {
      // if a scope is listed as a remote, it doesn't go to the hub
      return !this.get(scope);
    }
  }, {
    key: 'fetch',
    value: function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(ids, thisScope) {
        var _this2 = this;

        var withoutDeps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
        var context = arguments[3];
        var byScope, promises, bits;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                // 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
                byScope = (0, (_ramda || _load_ramda()).groupBy)((0, (_ramda || _load_ramda()).prop)('scope'));
                promises = [];

                (0, (_utils || _load_utils()).forEach)(byScope(ids), function (scopeIds, scopeName) {
                  promises.push(_this2.resolve(scopeName, thisScope).then(function (remote) {
                    return remote.fetch((_bitId || _load_bitId()).BitIds.fromArray(scopeIds), withoutDeps, context);
                  }));
                });

                (_logger || _load_logger()).default.debug(`[-] Running fetch (withoutDeps: ${withoutDeps}) on a remote`);
                _context.next = 6;
                return Promise.all(promises);

              case 6:
                bits = _context.sent;

                (_logger || _load_logger()).default.debug('[-] Returning from a remote');
                return _context.abrupt('return', (0, (_utils || _load_utils()).flatten)(bits));

              case 9:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function fetch(_x3, _x4) {
        return _ref.apply(this, arguments);
      }

      return fetch;
    }()
  }, {
    key: 'latestVersions',
    value: function () {
      var _ref2 = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee2(ids, thisScope) {
        var _this3 = this;

        var byScope, promises, components, flattenComponents;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee2$(_context2) {
          while (1) {
            switch (_context2.prev = _context2.next) {
              case 0:
                byScope = (0, (_ramda || _load_ramda()).groupBy)((0, (_ramda || _load_ramda()).prop)('scope'));
                promises = [];

                (0, (_utils || _load_utils()).forEach)(byScope(ids), function (scopeIds, scopeName) {
                  promises.push(_this3.resolve(scopeName, thisScope).then(function (remote) {
                    return remote.latestVersions(scopeIds);
                  }));
                });
                _context2.next = 5;
                return Promise.all(promises);

              case 5:
                components = _context2.sent;
                flattenComponents = (0, (_utils || _load_utils()).flatten)(components);
                return _context2.abrupt('return', flattenComponents.map(function (componentId) {
                  return (_bitId || _load_bitId()).BitId.parse(componentId, true);
                }));

              case 8:
              case 'end':
                return _context2.stop();
            }
          }
        }, _callee2, this);
      }));

      function latestVersions(_x5, _x6) {
        return _ref2.apply(this, arguments);
      }

      return latestVersions;
    }()
  }, {
    key: 'toPlainObject',
    value: function toPlainObject() {
      var object = {};

      this.forEach(function (remote) {
        var name = remote.name;
        if (remote.primary) name = (0, (_utils || _load_utils()).prependBang)(remote.name);
        object[name] = remote.host;
      });

      return object;
    }
  }], [{
    key: 'getScopeRemote',
    value: function getScopeRemote(scopeName) {
      return (_globalConfig || _load_globalConfig()).GlobalRemotes.load().then(function (globalRemotes) {
        return globalRemotes.toPlainObject();
      }).then(function (remotes) {
        return Remotes.load(remotes).resolve(scopeName);
      });
    }
  }, {
    key: 'load',
    value: function load(remotes) {
      var models = [];

      if (!remotes) return new Remotes();

      (0, (_utils || _load_utils()).forEach)(remotes, function (name, host) {
        var remote = (_remote || _load_remote()).default.load(name, host);
        models.push([remote.name, remote]);
      });

      return new Remotes(models);
    }
  }]);

  return Remotes;
}(_extendableBuiltin(Map));

exports.default = Remotes;