import {resolve} from 'path';
import {readFileSync, readdirSync} from 'fs';
import libnpmconfig from 'libnpmconfig';
import pacote from 'pacote';
import shell from 'shelljs';

import _ from 'lodash';

export const DEPS_GROUPS = [
  {name: 'global', field: 'dependencies', flag: 'g', ncuValue: 'prod'},
  {name: 'production', field: 'dependencies', flag: 'p', ncuValue: 'prod'},
  {name: 'optional', field: 'optionalDependencies', flag: 'o', ncuValue: 'optional'},
  {name: 'development', field: 'devDependencies', flag: 'd', ncuValue: 'dev'},
  {name: 'peer', field: 'peerDependencies', flag: 'r', ncuValue: 'peer'},
  {name: 'bundled', field: 'bundledDependencies', altField: 'bundleDependencies', flag: 'b', ncuValue: 'bundle'}
];

const getNpmConfig = _.memoize(() => {
  const config = {};

  libnpmconfig.read().forEach((value, key) => {
    if (typeof value === 'string') {
      // Replacing env ${VARS} in strings with the `process.env` values
      config[key] = value.replace(/\$\{(.+?)\}/gu, (_, envVar) =>
        process.env[envVar]
      );
    } else {
      config[key] = value;
    }
  });

  return config;
});

export function createGlobalPackageJson() {
  // retrieve the global package install path
  const res = shell.exec('npm root -g', {silent: true});
  if (res.code !== 0)
    throw new Error(`Could not determine npm's root path: ${res.stderr}`);

  const globalPath = res.stdout?.replace('\n', '');
  const pkg = { dependencies: {} };

  try {
    const modules = readdirSync(globalPath);
    for (const dir of modules)
    {
      try {
        const packageSource = readFileSync(resolve(globalPath, dir, 'package.json'), 'utf-8');
        const packageJson = JSON.parse(packageSource);
        if (packageJson.name && packageJson.version)
          pkg.dependencies[packageJson.name] = packageJson.version;
      } catch (err) {
        // package.json doesn't exist for this global module
        // or package.json couldn't be parsed
      }
    }
  } catch (err) {
    console.error(err);
  }

  return {content: pkg};
}

export function loadPackageJson() {
  const packageFile = resolve('./package.json');
  let packageJson;
  let packageSource;

  try {
    packageSource = readFileSync(packageFile, 'utf-8');
    packageJson = JSON.parse(packageSource);
  } catch (err) {
    console.error(`Error loading package.json: ${err.message}`);
    process.exit(1);
  }

  return {path: packageFile, content: packageJson, source: packageSource};
}

export function findModuleDepsGroup(moduleName, packageJson) {
  for (const {field, altField} of DEPS_GROUPS) {
    let modules = packageJson[field];

    if (!modules && altField) {
      modules = packageJson[altField];
    }

    if (modules && modules[moduleName]) {
      return modules;
    }
  }

  return null;
}

export function getModuleVersion(moduleName, packageJson) {
  const depsGroup = findModuleDepsGroup(moduleName, packageJson);

  return depsGroup ? depsGroup[moduleName] : null;
}

export function setModuleVersion(moduleName, newVersion, packageJson) {
  const depsGroup = findModuleDepsGroup(moduleName, packageJson);

  if (depsGroup) {
    depsGroup[moduleName] = newVersion;
    return true;
  } else {
    return false;
  }
}

export function getModuleHomepage(packageJson) {
  return packageJson.homepage || packageJson.url || null;
}

export const getModuleInfo = _.memoize(async moduleName =>
  pacote.manifest(moduleName, {
    ...getNpmConfig(),
    fullMetadata: true
  })
);

@@ -1463,100 +1463,49 @@
 %0A%0A  
-try %7B%0A    const modules = readdirSync(globalP
+function getPackageVersion(p
 ath)
-;
 %0A
-    for (const dir of modules)%0A  
   %7B%0A    
   tr
@@ -1500,26 +1500,22 @@
   %7B%0A    
-  
 try %7B%0A
-  
       co
@@ -1551,48 +1551,12 @@
 ync(
-resolve(globalPath, dir, 'package.json')
+path
 , 'u
@@ -1563,18 +1563,16 @@
 tf-8');%0A
-  
       co
@@ -1622,18 +1622,16 @@
 ;%0A      
- 
- 
 if (pack
@@ -1667,18 +1667,16 @@
 ersion)%0A
-  
         
@@ -1737,18 +1737,16 @@
 on;%0A    
- 
- 
 %7D catch 
@@ -1749,26 +1749,24 @@
 tch (err) %7B%0A
-  
       // pac
@@ -1812,18 +1812,16 @@
  module%0A
-  
       //
@@ -1860,19 +1860,462 @@
 sed%0A    
+%7D%0A
   %7D
+%0A%0A  try %7B%0A    const modules = readdirSync(globalPath);%0A    for (const dir of modules)%0A    %7B%0A      const dirPath = resolve(globalPath, dir);%0A%0A      if (dir.startsWith('@'))%0A      %7B%0A        const subModules = readdirSync(dirPath);%0A        for (const subDir of subModules)%0A        %7B%0A          getPackageVersion(resolve(dirPath, subDir, 'package.json'));%0A        %7D%0A      %7D%0A%0A      else%0A        getPackageVersion(resolve(dirPath, 'package.json'));
 %0A    %7D%0A 

Create re-usable getPackageVersion method