'use strict';
const assert = require('assert');
const semver = require('semver');
const pacote = require('pacote');

/**
 * @mixin
 * @alias env/composability
 */
const composability = module.exports;

composability.requireGenerator = function (namespace) {
  if (namespace === undefined) {
    return require('yeoman-generator');
  }
  // Namespace is a version
  if (semver.valid(namespace)) {
    return this.repository.requireModule('yeoman-generator', namespace);
  }
  return this.get(namespace);
};

/**
 * Install generators at the custom local repository and register.
 *
 * @param  {Object} packages - packages to install key(packageName): value(versionRange).
 * @return  {Boolean} - true if the install succeeded.
 */
composability.installLocalGenerators = async function (packages) {
  const entries = Object.entries(packages).filter(([packageName, version]) => !this.repository.verifyInstalledVersion(packageName, version));
  if (entries.length === 0) {
    return true;
  }
  const toInstall = {};
  entries.forEach(([packageName, version]) => {
    toInstall[packageName] = version;
  });
  if (await this.repository.installPackages(toInstall)) {
    const packagesToLookup = entries.map(([packageName, _]) => packageName);
    this.lookupLocalPackages(packagesToLookup);
    return true;
  }
  return false;
};

/**
 * Lookup and register generators from the custom local repository.
 *
 * @param  {String[]} [packagesToLookup='generator-*'] - packages to lookup.
 */
composability.lookupLocalPackages = function (packagesToLookup = 'generator-*') {
  this.lookup({packagePatterns: packagesToLookup, npmPaths: this.repository.nodeModulesPath});
};

/**
 * Resolve a package name with version.
 *
 * @param  {string} packageName - package to resolve.
 * @param  {string} [packageVersion] - version or range to resolve.
 * @param  {string[]} Array of key, value pairs.
 */
composability.resolvePackage = async function (packageName, packageVersion) {
  assert(packageName, 'Parameter packageName is required');
  if (packageVersion) {
    packageName = `${packageName}@${packageVersion}`;
  }
  const manifest = await pacote.manifest(packageName);
  if (!manifest) {
    return undefined;
  }
  const from = manifest._from;
  const index = from.lastIndexOf('@');
  if (index > 1) {
    const resolvedVersion = from.slice(index + 1, from.length) || manifest.version;
    return [from.slice(0, Math.max(0, index)), resolvedVersion];
  }
  return [manifest.name, from || manifest.version];
};