import "../../../../constants/error-messages.js";
import { GlobalConfig } from "../../../../config/global.js";
import { uniqueStrings } from "../../../../util/string.js";
import { logger } from "../../../../logger/index.js";
import { parseSingleYaml } from "../../../../util/yaml.js";
import { deleteLocalFile, ensureCacheDir, getSiblingFileName, localPathExists, readLocalFile } from "../../../../util/fs/index.js";
import { exec, getToolSettingsOptions } from "../../../../util/exec/index.js";
import { PNPM_CACHE_DIR, PNPM_STORE_DIR } from "../constants.js";
import { getNodeToolConstraint } from "./node-version.js";
import { getNodeOptions, getPackageManagerVersion, lazyLoadPackageJson } from "./utils.js";
import { isNumber, isNumericString } from "@sindresorhus/is";
import { quote } from "shlex";
import upath from "upath";
//#region lib/modules/manager/npm/post-update/pnpm.ts
function getPnpmConstraintFromUpgrades(upgrades) {
	for (const upgrade of upgrades) if (upgrade.depName === "pnpm" && upgrade.newVersion) return upgrade.newVersion;
	return null;
}
async function generateLockFile(lockFileDir, env, config, upgrades = []) {
	const lockFileName = upath.join(lockFileDir, "pnpm-lock.yaml");
	logger.debug(`Spawning pnpm install to create ${lockFileName}`);
	let lockFile = null;
	const commands = [];
	try {
		const lazyPgkJson = lazyLoadPackageJson(lockFileDir);
		const pnpmToolConstraint = {
			toolName: "pnpm",
			constraint: getPnpmConstraintFromUpgrades(upgrades) ?? config.constraints?.pnpm ?? getPackageManagerVersion("pnpm", await lazyPgkJson.getValue()) ?? await getConstraintFromLockFile(lockFileName)
		};
		const pnpmConfigCacheDir = await ensureCacheDir(PNPM_CACHE_DIR);
		const pnpmConfigStoreDir = await ensureCacheDir(PNPM_STORE_DIR);
		const extraEnv = {
			NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
			npm_config_store: env.npm_config_store,
			npm_config_cache_dir: pnpmConfigCacheDir,
			npm_config_store_dir: pnpmConfigStoreDir,
			pnpm_config_cache_dir: pnpmConfigCacheDir,
			pnpm_config_store_dir: pnpmConfigStoreDir
		};
		const { nodeMaxMemory } = getToolSettingsOptions(config.toolSettings);
		if (nodeMaxMemory) extraEnv.NODE_OPTIONS = getNodeOptions(nodeMaxMemory);
		const execOptions = {
			cwdFile: lockFileName,
			extraEnv,
			docker: {},
			toolConstraints: [await getNodeToolConstraint(config, upgrades, lockFileDir, lazyPgkJson), pnpmToolConstraint]
		};
		/* v8 ignore next 4 -- needs test */
		if (GlobalConfig.get("exposeAllEnv")) {
			extraEnv.NPM_AUTH = env.NPM_AUTH;
			extraEnv.NPM_EMAIL = env.NPM_EMAIL;
		}
		const pnpmWorkspaceFilePath = getSiblingFileName(lockFileName, "pnpm-workspace.yaml");
		let args = "--lockfile-only";
		if (await localPathExists(pnpmWorkspaceFilePath)) {
			const pnpmWorkspace = parseSingleYaml(await readLocalFile(pnpmWorkspaceFilePath, "utf8"));
			if (pnpmWorkspace?.packages?.length) {
				logger.debug(`Found pnpm workspace with ${pnpmWorkspace.packages.length} package definitions`);
				args += " --recursive";
			}
		}
		if (!GlobalConfig.get("allowScripts")) {
			args += " --ignore-scripts";
			args += " --ignore-pnpmfile";
		} else if (config.ignoreScripts) args += " --ignore-scripts";
		logger.trace({ args }, "pnpm command options");
		const lockUpdates = upgrades.filter((upgrade) => upgrade.isLockfileUpdate);
		if (lockUpdates.length !== upgrades.length) commands.push(`pnpm install ${args}`);
		if (lockUpdates.length) {
			logger.debug("Performing lockfileUpdate (pnpm)");
			commands.push(`pnpm update --no-save ${lockUpdates.map((update) => `${update.packageName}@${update.newVersion}`).filter(uniqueStrings).map(quote).join(" ")} ${args}`);
		}
		if (config.postUpdateOptions?.includes("pnpmDedupe")) commands.push("pnpm dedupe --ignore-scripts");
		if (upgrades.find((upgrade) => upgrade.isLockFileMaintenance)) {
			logger.debug(`Removing ${lockFileName} first due to lock file maintenance upgrade`);
			try {
				await deleteLocalFile(lockFileName);
			} catch (err) 			/* v8 ignore next -- TODO: add test #40625 */ {
				logger.debug({
					err,
					lockFileName
				}, "Error removing `pnpm-lock.yaml` for lock file maintenance");
			}
		}
		await exec(commands, execOptions);
		lockFile = await readLocalFile(lockFileName, "utf8");
	} catch (err) {
		// v8 ignore if -- TODO: add test #40625
		if (err.message === "temporary-error") throw err;
		logger.debug({
			commands,
			err,
			stdout: err.stdout,
			stderr: err.stderr,
			type: "pnpm"
		}, "lock file error");
		return {
			error: true,
			stderr: err.stderr,
			stdout: err.stdout
		};
	}
	return { lockFile };
}
async function getConstraintFromLockFile(lockFileName) {
	let constraint = null;
	try {
		const lockfileContent = await readLocalFile(lockFileName, "utf8");
		if (!lockfileContent) {
			logger.trace(`Empty pnpm lock file: ${lockFileName}`);
			return null;
		}
		const pnpmLock = parseSingleYaml(lockfileContent);
		if (!isNumber(pnpmLock?.lockfileVersion) && !isNumericString(pnpmLock?.lockfileVersion)) {
			logger.trace(`Invalid pnpm lockfile version: ${lockFileName}`);
			return null;
		}
		const { lowerConstraint, upperConstraint } = lockToPnpmVersionMapping.find((m) => m.lockfileVersion === pnpmLock.lockfileVersion) ?? {
			lockfileVersion: 5,
			lowerConstraint: ">=3",
			upperConstraint: "<3.5.0"
		};
		constraint = lowerConstraint;
		if (upperConstraint) constraint += ` ${upperConstraint}`;
	} catch (err) {
		logger.warn({ err }, "Error getting pnpm constraints from lock file");
	}
	return constraint;
}
/**
* pnpm lockfiles have corresponding version numbers called "lockfileVersion"
* each lockfileVersion can only be generated by a certain pnpm version range
* eg. lockfileVersion: 5.4 can only be generated by pnpm version >=7 && <8
* official list can be found here : https://github.com/pnpm/spec/tree/master/lockfile
* we use the mapping present below to find the compatible pnpm version range for a given lockfileVersion
*
* the various terms used in the mapping are explained below:
* lowerConstraint : lowest pnpm version that can generate the lockfileVersion
* upperConstraint : highest pnpm version that can generate the lockfileVersion
* lowerBound      : highest pnpm version that is less than the lowerConstraint
* upperBound      : lowest pnpm version that is greater than the upperConstraint
*
* To handle future lockfileVersions, we need to:
* 1. add a upperBound and upperConstraint to the current latest lockfileVersion
* 2. add an object for the new lockfileVersion with lowerBound and lowerConstraint
*
* lockfileVersion from v6 on are strings
*/
const lockToPnpmVersionMapping = [
	{
		lockfileVersion: "9.0",
		lowerConstraint: ">=9"
	},
	{
		lockfileVersion: "6.0",
		lowerConstraint: ">=7.24.2",
		upperConstraint: "<9"
	},
	{
		lockfileVersion: 5.4,
		lowerConstraint: ">=7",
		upperConstraint: "<8"
	},
	{
		lockfileVersion: 5.3,
		lowerConstraint: ">=6",
		upperConstraint: "<7"
	},
	{
		lockfileVersion: 5.2,
		lowerConstraint: ">=5.10.0",
		upperConstraint: "<6"
	},
	{
		lockfileVersion: 5.1,
		lowerConstraint: ">=3.5.0",
		upperConstraint: "<5.9.3"
	}
];
//#endregion
export { generateLockFile, getConstraintFromLockFile };

//# sourceMappingURL=pnpm.js.map