import { TEMPORARY_ERROR } from "../../../constants/error-messages.js";
import { regEx } from "../../../util/regex.js";
import { GlobalConfig } from "../../../config/global.js";
import { logger } from "../../../logger/index.js";
import { findUpLocal, readLocalFile, writeLocalFile } from "../../../util/fs/index.js";
import { exec } from "../../../util/exec/index.js";
import { getFiles, getRepoStatus } from "../../../util/git/index.js";
import { scm } from "../../platform/scm.js";
import { isGradleBuildFile } from "./utils.js";
import { extraEnv, extractGradleVersion, getJavaConstraint, gradleWrapperFileName, prepareGradleCommand } from "../gradle-wrapper/utils.js";
import { isGcvLockFile, isGcvPropsFile } from "./extract/consistent-versions-plugin.js";
import { isNonEmptyStringAndNotWhitespace } from "@sindresorhus/is";
import upath from "upath";
import { quote } from "shlex";

//#region lib/modules/manager/gradle/artifacts.ts
function isGradleExecutionAllowed(command) {
	if (!GlobalConfig.get("allowedUnsafeExecutions", []).includes("gradleWrapper")) {
		logger.once.warn(`Gradle wrapper command, \`${command}\`, was requested to run, but \`gradleWrapper\` is not permitted in the allowedUnsafeExecutions`);
		return false;
	}
	return true;
}
function isLockFile(fileName) {
	return fileName.endsWith(".lockfile") || isGcvLockFile(fileName);
}
async function getUpdatedLockfiles(oldLockFileContentMap) {
	const res = [];
	const status = await getRepoStatus();
	for (const modifiedFile of status.modified) if (isLockFile(modifiedFile) || modifiedFile.endsWith("gradle/verification-metadata.xml")) {
		const newContent = await readLocalFile(modifiedFile, "utf8");
		if (oldLockFileContentMap[modifiedFile] !== newContent) res.push({ file: {
			type: "addition",
			path: modifiedFile,
			contents: newContent
		} });
	}
	return res;
}
async function getSubProjectList(cmd, execOptions) {
	const subprojects = [""];
	const subprojectsRegex = regEx(/^[ \t]*subprojects: \[(?<subprojects>.+)\]/m);
	const subprojectsMatch = (await exec(`${cmd} properties`, execOptions)).stdout.match(subprojectsRegex);
	if (subprojectsMatch?.groups?.subprojects) {
		const projectRegex = regEx(/project '(?<name>.+?)'/g);
		const matches = subprojectsMatch.groups.subprojects.matchAll(projectRegex);
		for (const match of matches) if (match?.groups?.name) subprojects.push(match.groups.name);
	}
	return subprojects;
}
async function getGradleVersion(gradlewFile) {
	const extractResult = extractGradleVersion(await readLocalFile(upath.join(upath.dirname(gradlewFile), "gradle/wrapper/gradle-wrapper.properties"), "utf8") ?? "");
	return extractResult ? extractResult.version : null;
}
async function buildUpdateVerificationMetadataCmd(verificationMetadataFile, baseCmd) {
	if (!verificationMetadataFile) return null;
	const verificationMetadata = await readLocalFile(verificationMetadataFile);
	const verifiesChecksums = verificationMetadata?.includes("<verify-metadata>true</verify-metadata>");
	const verifiesSignatures = verificationMetadata?.includes("<verify-signatures>true</verify-signatures>");
	const hashTypes = ["sha256", "sha512"].filter((type) => verificationMetadata?.includes(`<${type} `));
	if (verifiesChecksums || hashTypes.length) logger.debug("Dependency metadata verification enabled or checksums present - generating checksums");
	if ((verifiesChecksums || verifiesSignatures) && !hashTypes.length) hashTypes.push("sha256");
	if (verifiesSignatures) {
		logger.debug("Dependency signature verification enabled - generating PGP signatures");
		hashTypes.push("pgp");
	}
	if (!hashTypes.length) return null;
	return `${baseCmd} --write-verification-metadata ${hashTypes.join(",")} dependencies`;
}
async function updateArtifacts({ packageFileName, updatedDeps, newPackageFileContent, config }) {
	logger.debug(`gradle.updateArtifacts(${packageFileName})`);
	const fileList = await scm.getFileList();
	const lockFiles = fileList.filter((file) => isLockFile(file));
	const verificationMetadataFile = fileList.find((fileName) => fileName.endsWith("gradle/verification-metadata.xml"));
	if (!lockFiles.length && !verificationMetadataFile) {
		logger.debug("No Gradle dependency lockfiles or verification metadata found - skipping update");
		return null;
	}
	const gradlewName = gradleWrapperFileName();
	const gradlewFile = await findUpLocal(gradlewName, upath.dirname(packageFileName));
	if (!gradlewFile) {
		logger.debug("Found Gradle dependency lockfiles but no gradlew - aborting update");
		return null;
	}
	if (config.isLockFileMaintenance && (!isGradleBuildFile(packageFileName) || upath.dirname(packageFileName) !== upath.dirname(gradlewFile))) {
		logger.trace("No build.gradle(.kts) file or not in root project - skipping lock file maintenance");
		return null;
	}
	if (!isGradleExecutionAllowed(gradlewFile)) {
		logger.trace("Not allowed to execute gradle due to allowedUnsafeExecutions - aborting update");
		return null;
	}
	logger.debug("Updating found Gradle dependency lockfiles");
	try {
		const oldLockFileContentMap = await getFiles(lockFiles);
		await prepareGradleCommand(gradlewFile);
		const baseCmd = `${gradlewName} --console=plain --dependency-verification lenient -q`;
		const execOptions = {
			cwdFile: gradlewFile,
			docker: {},
			extraEnv,
			toolConstraints: [{
				toolName: "java",
				constraint: config.constraints?.java ?? await getJavaConstraint(await getGradleVersion(gradlewFile), gradlewFile)
			}]
		};
		const cmds = [];
		if (lockFiles.length) {
			let lockfileCmd = `${baseCmd} ${(await getSubProjectList(baseCmd, execOptions)).map((project) => `${project}:dependencies`).map(quote).join(" ")}`;
			if (config.isLockFileMaintenance === true || !updatedDeps.length || isGcvPropsFile(packageFileName)) lockfileCmd += " --write-locks";
			else {
				const updatedDepNames = updatedDeps.map(({ depName, packageName }) => packageName ?? depName).filter(isNonEmptyStringAndNotWhitespace);
				lockfileCmd += ` --update-locks ${updatedDepNames.map(quote).join(",")}`;
			}
			cmds.push(lockfileCmd);
		}
		const updateVerificationMetadataCmd = await buildUpdateVerificationMetadataCmd(verificationMetadataFile, baseCmd);
		if (updateVerificationMetadataCmd) cmds.push(updateVerificationMetadataCmd);
		if (!cmds.length) {
			logger.debug("No lockfile or verification metadata update necessary");
			return null;
		}
		await writeLocalFile(packageFileName, newPackageFileContent);
		await exec(cmds, {
			...execOptions,
			ignoreStdout: true
		});
		const res = await getUpdatedLockfiles(oldLockFileContentMap);
		logger.debug("Returning updated Gradle dependency lockfiles");
		return res.length > 0 ? res : null;
	} catch (err) {
		if (err.message === TEMPORARY_ERROR) throw err;
		logger.debug({ err }, "Error while updating Gradle dependency lockfiles");
		return [{ artifactError: {
			lockFile: packageFileName,
			stderr: err.message
		} }];
	}
}

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