"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 = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

function _dependency() {
  const data = _interopRequireDefault(require("./dependency"));

  _dependency = function () {
    return data;
  };

  return data;
}

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

  _bitId = function () {
    return data;
  };

  return data;
}

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

  _utils = function () {
    return data;
  };

  return data;
}

function _validationError() {
  const data = _interopRequireDefault(require("../../../error/validation-error"));

  _validationError = function () {
    return data;
  };

  return data;
}

function _validateType() {
  const data = _interopRequireDefault(require("../../../utils/validate-type"));

  _validateType = function () {
    return data;
  };

  return data;
}

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

  _scopeRemotes = function () {
    return data;
  };

  return data;
}

class Dependencies {
  constructor(dependencies = []) {
    this.dependencies = dependencies;
  }

  serialize() {
    return this.dependencies.map(dep => Object.assign({}, dep, {
      id: dep.id.toString()
    }));
  }

  get() {
    return this.dependencies;
  }

  sort() {
    this.dependencies.sort((a, b) => {
      const idA = a.id.toString();
      const idB = b.id.toString();

      if (idA < idB) {
        return -1;
      }

      if (idA > idB) {
        return 1;
      }

      return 0;
    });
  }

  getClone() {
    return this.dependencies.map(dependency => _dependency().default.getClone(dependency));
  }

  add(dependency) {
    this.dependencies.push(dependency);
  }

  toStringOfIds() {
    return this.dependencies.map(dep => dep.id.toString());
  }

  isEmpty() {
    return !this.dependencies.length;
  }

  asWritableObject() {
    return _ramda().default.mergeAll(this.dependencies.map(dependency => dependency.id.toObject()));
  }

  cloneAsString() {
    return this.dependencies.map(dependency => {
      const dependencyClone = _ramda().default.clone(dependency);

      dependencyClone.id = dependency.id.toString();
      return dependencyClone;
    });
  }

  cloneAsObject() {
    return this.dependencies.map(dependency => {
      const dependencyClone = _ramda().default.clone(dependency);

      dependencyClone.id = dependency.id.serialize();
      return dependencyClone;
    });
  }

  stripOriginallySharedDir(manipulateDirData, originallySharedDir) {
    this.dependencies.forEach(dependency => {
      _dependency().default.stripOriginallySharedDir(dependency, manipulateDirData, originallySharedDir);
    });
  }

  addWrapDir(manipulateDirData, wrapDir) {
    this.dependencies.forEach(dependency => {
      _dependency().default.addWrapDir(dependency, manipulateDirData, wrapDir);
    });
  }
  /**
   * needed for calculating the originallySharedDir. when isCustomResolveUsed, don't take into
   * account the dependencies as they don't have relative paths
   */


  getSourcesPaths() {
    return _ramda().default.flatten(this.dependencies.map(dependency => dependency.relativePaths.map(relativePath => {
      return relativePath.isCustomResolveUsed ? null : relativePath.sourceRelativePath;
    }).filter(x => x)));
  }

  getById(id) {
    return this.dependencies.find(dep => dep.id.isEqual(id));
  }

  getByIdStr(id) {
    return this.dependencies.find(dep => dep.id.toString() === id);
  }

  getBySourcePath(sourcePath) {
    return this.dependencies.find(dependency => dependency.relativePaths.some(relativePath => {
      return relativePath.sourceRelativePath === sourcePath;
    }));
  }

  getAllIds() {
    return _bitId().BitIds.fromArray(this.dependencies.map(dependency => dependency.id));
  }

  addRemoteAndLocalVersions(scope, modelDependencies) {
    var _this = this;

    return (0, _bluebird().coroutine)(function* () {
      const dependenciesIds = _this.dependencies.map(dependency => dependency.id);

      const localDependencies = yield scope.latestVersions(dependenciesIds);
      const remoteVersionsDependencies = yield (0, _scopeRemotes().fetchRemoteVersions)(scope, dependenciesIds);

      _this.dependencies.forEach(dependency => {
        const remoteVersionId = remoteVersionsDependencies.find(remoteId => remoteId.isEqualWithoutVersion(dependency.id));
        const localVersionId = localDependencies.find(localId => localId.isEqualWithoutVersion(dependency.id));
        const modelVersionId = modelDependencies.get().find(modelDependency => modelDependency.id.isEqualWithoutVersion(dependency.id)); // $FlowFixMe

        dependency.remoteVersion = remoteVersionId ? remoteVersionId.version : null; // $FlowFixMe

        dependency.localVersion = localVersionId ? localVersionId.version : null; // $FlowFixMe

        dependency.currentVersion = modelVersionId ? modelVersionId.id.version : dependency.id.version;
      });
    })();
  }

  getCustomResolvedData() {
    const importSourceMap = {};
    this.dependencies.forEach(dependency => {
      dependency.relativePaths.forEach(relativePath => {
        if (relativePath.isCustomResolveUsed) {
          if (!relativePath.importSource) {
            throw new Error(`${dependency.id.toString()} relativePath.importSource must be set when relativePath.isCustomResolveUsed`);
          }

          importSourceMap[relativePath.importSource] = dependency.id;
        }
      });
    });
    return importSourceMap;
  }

  isCustomResolvedUsed() {
    return this.dependencies.some(dependency => {
      return dependency.relativePaths.some(relativePath => relativePath.isCustomResolveUsed);
    });
  }

  validate() {
    let message = 'failed validating the dependencies.';
    (0, _validateType().default)(message, this.dependencies, 'dependencies', 'array');
    const allIds = this.getAllIds();
    this.dependencies.forEach(dependency => {
      (0, _validateType().default)(message, dependency, 'dependency', 'object');
      if (!dependency.id) throw new (_validationError().default)('one of the dependencies is missing ID');

      if (!dependency.relativePaths) {
        throw new (_validationError().default)(`a dependency ${dependency.id.toString()} is missing relativePaths`);
      }

      const sameIds = allIds.filterExact(dependency.id);

      if (sameIds.length > 1) {
        throw new (_validationError().default)(`a dependency ${dependency.id.toString()} is duplicated`);
      }

      const permittedProperties = ['id', 'relativePaths'];
      const currentProperties = Object.keys(dependency);
      currentProperties.forEach(currentProp => {
        if (!permittedProperties.includes(currentProp)) {
          throw new (_validationError().default)(`a dependency ${dependency.id.toString()} has an undetected property "${currentProp}"`);
        }
      });
      (0, _validateType().default)(message, dependency.relativePaths, 'dependency.relativePaths', 'array');
      dependency.relativePaths.forEach(relativePath => {
        message = `failed validating dependency ${dependency.id.toString()}.`;
        (0, _validateType().default)(message, dependency, 'dependency', 'object');
        const requiredProps = ['sourceRelativePath', 'destinationRelativePath'];
        const pathProps = ['sourceRelativePath', 'destinationRelativePath'];
        const optionalProps = ['importSpecifiers', 'isCustomResolveUsed', 'importSource'];
        const allProps = requiredProps.concat(optionalProps);
        requiredProps.forEach(prop => {
          if (!relativePath[prop]) {
            throw new (_validationError().default)(`${message} relativePaths.${prop} is missing`);
          }
        });
        pathProps.forEach(prop => {
          if (!(0, _utils().isValidPath)(relativePath[prop])) {
            throw new (_validationError().default)(`${message} relativePaths.${prop} has an invalid path ${relativePath[prop]}`);
          }
        });
        Object.keys(relativePath).forEach(prop => {
          if (!allProps.includes(prop)) {
            throw new (_validationError().default)(`${message} undetected property of relativePaths "${prop}"`);
          }
        });

        if (relativePath.isCustomResolveUsed) {
          if (!relativePath.importSource) {
            throw new (_validationError().default)(`a dependency ${dependency.id.toString()} is missing relativePath.importSource`);
          }

          (0, _validateType().default)(message, relativePath.importSource, 'relativePath.importSource', 'string');
        }

        if (relativePath.importSpecifiers) {
          (0, _validateType().default)(message, relativePath.importSpecifiers, 'relativePath.importSpecifiers', 'array'); // $FlowFixMe it's already confirmed that relativePath.importSpecifiers is set

          relativePath.importSpecifiers.forEach(importSpecifier => {
            (0, _validateType().default)(message, importSpecifier, 'importSpecifier', 'object');

            if (!importSpecifier.mainFile) {
              throw new (_validationError().default)(`${message} mainFile property is missing from the importSpecifier`);
            }

            const specifierProps = ['isDefault', 'name'].sort().toString();
            const mainFileProps = Object.keys(importSpecifier.mainFile).sort().toString();

            if (mainFileProps !== specifierProps) {
              throw new (_validationError().default)(`${message} expected properties of importSpecifier.mainFile "${specifierProps}", got "${mainFileProps}"`);
            }

            if (importSpecifier.linkFile) {
              const linkFileProps = Object.keys(importSpecifier.linkFile).sort().toString();

              if (linkFileProps !== specifierProps) {
                throw new (_validationError().default)(`${message} expected properties of importSpecifier.linkFile "${specifierProps}", got "${linkFileProps}"`);
              }
            }

            const specifierPermittedProps = ['mainFile', 'linkFile'];
            Object.keys(importSpecifier).forEach(prop => {
              if (!specifierPermittedProps.includes(prop)) {
                throw new (_validationError().default)(`${message} undetected property of importSpecifier "${prop}"`);
              }
            });
          });
        }
      });
    });
  }

}

exports.default = Dependencies;