import { SYSTEM_INSUFFICIENT_DISK_SPACE, TEMPORARY_ERROR } from "../../../constants/error-messages.js";
import { regEx } from "../../../util/regex.js";
import { coerceString } from "../../../util/string.js";
import { logger } from "../../../logger/index.js";
import { find, findAll } from "../../../util/host-rules.js";
import { deleteLocalFile, ensureCacheDir, ensureLocalDir, getSiblingFileName, localPathExists, readLocalFile, writeLocalFile } from "../../../util/fs/index.js";
import { Json } from "../../../util/schema-utils/index.js";
import { GitTagsDatasource } from "../../datasource/git-tags/index.js";
import { exec } from "../../../util/exec/index.js";
import { PackagistDatasource } from "../../datasource/packagist/index.js";
import { getRepoStatus } from "../../../util/git/index.js";
import { findGithubToken, takePersonalAccessTokenIfPossible } from "../../../util/check-token.js";
import { Lockfile, PackageFile } from "./schema.js";
import { extractConstraints, getComposerArguments, getComposerUpdateArguments, getPhpConstraint, isArtifactAuthEnabled, requireComposerDependencyInstallation } from "./utils.js";
import { isEmptyObject, isString } from "@sindresorhus/is";
import { z } from "zod";
import { quote } from "shlex";

//#region lib/modules/manager/composer/artifacts.ts
function getAuthJson() {
	const authJson = {};
	const githubHostRule = find({
		hostType: "github",
		url: "https://api.github.com/"
	});
	const gitTagsHostRule = find({
		hostType: GitTagsDatasource.id,
		url: "https://github.com"
	});
	const selectedGithubToken = takePersonalAccessTokenIfPossible(isArtifactAuthEnabled(githubHostRule) ? findGithubToken(githubHostRule) : void 0, isArtifactAuthEnabled(gitTagsHostRule) ? findGithubToken(gitTagsHostRule) : void 0);
	if (selectedGithubToken) authJson["github-oauth"] = { "github.com": selectedGithubToken };
	for (const gitlabHostRule of findAll({ hostType: "gitlab" })) {
		if (!isArtifactAuthEnabled(gitlabHostRule)) continue;
		if (gitlabHostRule?.token) {
			const host = coerceString(gitlabHostRule.resolvedHost, "gitlab.com");
			authJson["gitlab-token"] = authJson["gitlab-token"] ?? {};
			authJson["gitlab-token"][host] = gitlabHostRule.token;
			authJson["gitlab-domains"] = [host, ...authJson["gitlab-domains"] ?? []];
		}
	}
	for (const packagistHostRule of findAll({ hostType: PackagistDatasource.id })) {
		if (!isArtifactAuthEnabled(packagistHostRule)) continue;
		const { resolvedHost, username, password, token } = packagistHostRule;
		if (resolvedHost && username && password) {
			authJson["http-basic"] = authJson["http-basic"] ?? {};
			authJson["http-basic"][resolvedHost] = {
				username,
				password
			};
		} else if (resolvedHost && token) {
			authJson.bearer = authJson.bearer ?? {};
			authJson.bearer[resolvedHost] = token;
		}
	}
	return isEmptyObject(authJson) ? null : JSON.stringify(authJson);
}
async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, config }) {
	logger.debug(`composer.updateArtifacts(${packageFileName})`);
	const file = Json.pipe(PackageFile).parse(newPackageFileContent);
	const lockFileName = packageFileName.replace(regEx(/\.json$/), ".lock");
	const lockfile = await z.string().transform((f) => readLocalFile(f, "utf8")).pipe(Json).pipe(Lockfile).nullable().catch(null).parseAsync(lockFileName);
	if (!lockfile) {
		logger.debug("Composer: unable to read lockfile");
		return null;
	}
	const vendorDir = getSiblingFileName(packageFileName, "vendor");
	const commitVendorFiles = await localPathExists(vendorDir);
	await ensureLocalDir(vendorDir);
	try {
		await writeLocalFile(packageFileName, newPackageFileContent);
		const constraints = {
			...extractConstraints(file, lockfile),
			...config.constraints
		};
		const composerToolConstraint = {
			toolName: "composer",
			constraint: constraints.composer
		};
		const phpToolConstraint = {
			toolName: "php",
			constraint: getPhpConstraint(constraints)
		};
		const execOptions = {
			cwdFile: packageFileName,
			extraEnv: {
				COMPOSER_CACHE_DIR: await ensureCacheDir("composer"),
				COMPOSER_AUTH: getAuthJson()
			},
			toolConstraints: [phpToolConstraint, composerToolConstraint],
			docker: {}
		};
		const commands = [];
		if (requireComposerDependencyInstallation(lockfile)) {
			const preCmd = "composer";
			const preArgs = "install" + getComposerArguments(config, composerToolConstraint);
			logger.trace({
				preCmd,
				preArgs
			}, "composer pre-update command");
			commands.push("git stash -- composer.json");
			commands.push(`${preCmd} ${preArgs}`);
			commands.push({
				command: [
					"git",
					"stash",
					"pop"
				],
				ignoreFailure: true
			});
		}
		if (config.isLockFileMaintenance) {
			logger.debug(`Removing ${lockFileName} first due to lock file maintenance upgrade`);
			await deleteLocalFile(lockFileName);
		}
		const cmd = "composer";
		let args;
		if (config.isLockFileMaintenance) args = "update";
		else args = ("update " + updatedDeps.map((dep) => dep.newVersion ? quote(`${dep.depName}:${dep.newVersion}`) : quote(dep.depName)).filter(isString).map((dep) => quote(dep)).join(" ")).trim() + (config.postUpdateOptions?.includes("composerWithAll") ? " --with-all-dependencies" : " --with-dependencies");
		args += getComposerUpdateArguments(config, composerToolConstraint);
		logger.trace({
			cmd,
			args
		}, "composer command");
		commands.push(`${cmd} ${args}`);
		await exec(commands, execOptions);
		const status = await getRepoStatus();
		if (!status.modified.includes(lockFileName)) return null;
		logger.debug("Returning updated composer.lock");
		const res = [{ file: {
			type: "addition",
			path: lockFileName,
			contents: await readLocalFile(lockFileName)
		} }];
		if (!commitVendorFiles) return res;
		logger.debug(`Committing vendor files in ${vendorDir}`);
		for (const f of [...status.modified, ...status.not_added]) if (f.startsWith(vendorDir)) res.push({ file: {
			type: "addition",
			path: f,
			contents: await readLocalFile(f)
		} });
		for (const f of status.deleted) res.push({ file: {
			type: "deletion",
			path: f
		} });
		return res;
	} catch (err) {
		// istanbul ignore if
		if (err.message === TEMPORARY_ERROR) throw err;
		if (err.message?.includes("Your requirements could not be resolved to an installable set of packages.")) logger.info("Composer requirements cannot be resolved");
		else if (err.message?.includes("write error (disk full?)")) throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE);
		else logger.debug({ err }, "Failed to generate composer.lock");
		return [{ artifactError: {
			lockFile: lockFileName,
			stderr: err.message
		} }];
	}
}

//#endregion
export { updateArtifacts };
//# sourceMappingURL=artifacts.js.map