'use strict';

const node_fs = require('node:fs');
const promises = require('node:fs/promises');
const node_path = require('node:path');

function normalizeWindowsPath(input = "") {
  if (!input || !input.includes("\\")) {
    return input;
  }
  return input.replace(/\\/g, "/");
}

const _UNC_REGEX = /^[/\\]{2}/;
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
const normalize = function(path) {
  if (path.length === 0) {
    return ".";
  }
  path = normalizeWindowsPath(path);
  const isUNCPath = path.match(_UNC_REGEX);
  const isPathAbsolute = isAbsolute(path);
  const trailingSeparator = path[path.length - 1] === "/";
  path = normalizeString(path, !isPathAbsolute);
  if (path.length === 0) {
    if (isPathAbsolute) {
      return "/";
    }
    return trailingSeparator ? "./" : ".";
  }
  if (trailingSeparator) {
    path += "/";
  }
  if (_DRIVE_LETTER_RE.test(path)) {
    path += "/";
  }
  if (isUNCPath) {
    if (!isPathAbsolute) {
      return `//./${path}`;
    }
    return `//${path}`;
  }
  return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
};
const join = function(...arguments_) {
  if (arguments_.length === 0) {
    return ".";
  }
  let joined;
  for (const argument of arguments_) {
    if (argument && argument.length > 0) {
      if (joined === void 0) {
        joined = argument;
      } else {
        joined += `/${argument}`;
      }
    }
  }
  if (joined === void 0) {
    return ".";
  }
  return normalize(joined.replace(/\/\/+/g, "/"));
};
function cwd() {
  if (typeof process !== "undefined") {
    return process.cwd().replace(/\\/g, "/");
  }
  return "/";
}
const resolve = function(...arguments_) {
  arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
  let resolvedPath = "";
  let resolvedAbsolute = false;
  for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
    const path = index >= 0 ? arguments_[index] : cwd();
    if (!path || path.length === 0) {
      continue;
    }
    resolvedPath = `${path}/${resolvedPath}`;
    resolvedAbsolute = isAbsolute(path);
  }
  resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
  if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
    return `/${resolvedPath}`;
  }
  return resolvedPath.length > 0 ? resolvedPath : ".";
};
function normalizeString(path, allowAboveRoot) {
  let res = "";
  let lastSegmentLength = 0;
  let lastSlash = -1;
  let dots = 0;
  let char = null;
  for (let index = 0; index <= path.length; ++index) {
    if (index < path.length) {
      char = path[index];
    } else if (char === "/") {
      break;
    } else {
      char = "/";
    }
    if (char === "/") {
      if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
        if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
          if (res.length > 2) {
            const lastSlashIndex = res.lastIndexOf("/");
            if (lastSlashIndex === -1) {
              res = "";
              lastSegmentLength = 0;
            } else {
              res = res.slice(0, lastSlashIndex);
              lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
            }
            lastSlash = index;
            dots = 0;
            continue;
          } else if (res.length > 0) {
            res = "";
            lastSegmentLength = 0;
            lastSlash = index;
            dots = 0;
            continue;
          }
        }
        if (allowAboveRoot) {
          res += res.length > 0 ? "/.." : "..";
          lastSegmentLength = 2;
        }
      } else {
        if (res.length > 0) {
          res += `/${path.slice(lastSlash + 1, index)}`;
        } else {
          res = path.slice(lastSlash + 1, index);
        }
        lastSegmentLength = index - lastSlash - 1;
      }
      lastSlash = index;
      dots = 0;
    } else if (char === "." && dots !== -1) {
      ++dots;
    } else {
      dots = -1;
    }
  }
  return res;
}
const isAbsolute = function(p) {
  return _IS_ABSOLUTE_RE.test(p);
};

const packageManagers = [
  { name: "npm", command: "npm", lockFile: "package-lock.json" },
  {
    name: "pnpm",
    command: "pnpm",
    lockFile: "pnpm-lock.yaml",
    files: ["pnpm-workspace.yaml"]
  },
  {
    name: "yarn",
    command: "yarn",
    majorVersion: "1.0.0",
    lockFile: "yarn.lock"
  },
  {
    name: "yarn",
    command: "yarn",
    majorVersion: "3.0.0",
    lockFile: "yarn.lock",
    files: [".yarnrc.yml"]
  }
];
async function detectPackageManager(cwd, options = {}) {
  const detected = await findup(cwd, async (path) => {
    if (!options.ignorePackageJSON) {
      const packageJSONPath = join(path, "package.json");
      if (node_fs.existsSync(packageJSONPath)) {
        const packageJSON = JSON.parse(await promises.readFile(packageJSONPath, "utf8"));
        if (packageJSON?.packageManager) {
          const [name, version = "0.0.0"] = packageJSON.packageManager.split("@");
          const majorVersion = version.split(".")[0];
          const packageManager = packageManagers.find(
            (pm) => pm.name === name && pm.majorVersion === majorVersion
          ) || packageManagers.find((pm) => pm.name === name);
          return {
            ...packageManager,
            name,
            command: name,
            version,
            majorVersion
          };
        }
      }
    }
    if (!options.ignoreLockFile) {
      for (const packageManager of packageManagers) {
        const detectionsFiles = [
          packageManager.lockFile,
          ...packageManager.files || []
        ].filter(Boolean);
        if (detectionsFiles.some((file) => node_fs.existsSync(node_path.resolve(path, file)))) {
          return {
            ...packageManager
          };
        }
      }
    }
  });
  return detected;
}
async function findup(cwd, match) {
  const segments = normalize(cwd).split("/");
  while (segments.length > 0) {
    const path = segments.join("/");
    const result = await match(path);
    if (result) {
      return result;
    }
    segments.pop();
  }
}

function runCorepack(pm, argv, options = {}) {
  if (pm === "npm") {
    return runCommand("npm", argv, options);
  }
  return runCommand("corepack", [pm, ...argv], options);
}
async function runCommand(command, argv, options = {}) {
  const { execa } = await import('execa');
  try {
    await execa(command, argv, {
      cwd: resolve(options.cwd || process.cwd()),
      stdio: options.silent ? "ignore" : "inherit"
    });
  } catch {
    return false;
  }
  return true;
}

async function addDependency(name, _options = {}) {
  const options = await _resolveOptions(_options);
  const args = [
    options.packageManager.name === "npm" ? "install" : "add",
    options.workspace ? options.packageManager.name === "yarn" ? "-W" : "-w" : "",
    options.dev ? "-D" : "",
    name
  ].filter(Boolean);
  await runCorepack(options.packageManager.command, args, {
    cwd: options.cwd,
    silent: options.silent
  });
  return {};
}
async function addDevDependency(name, _options = {}) {
  return await addDependency(name, { ..._options, dev: true });
}
async function removeDependency(name, _options = {}) {
  const options = await _resolveOptions(_options);
  const args = [
    options.packageManager.name === "npm" ? "uninstall" : "remove",
    options.dev ? "-D" : "",
    name
  ].filter(Boolean);
  await runCorepack(options.packageManager.command, args, {
    cwd: options.cwd,
    silent: options.silent
  });
  return {};
}
async function _resolveOptions(options = {}) {
  options.cwd = options.cwd || process.cwd();
  options.silent = options.silent ?? process.env.NODE_ENV === "test";
  if (!options.packageManager) {
    const detected = await detectPackageManager(options.cwd);
    options.packageManager = detected;
  }
  return options;
}

exports.addDependency = addDependency;
exports.addDevDependency = addDevDependency;
exports.detectPackageManager = detectPackageManager;
exports.removeDependency = removeDependency;
