import { MANAGER_LOCKFILE_ERROR, PLATFORM_AUTHENTICATION_ERROR, PLATFORM_BAD_CREDENTIALS, REPOSITORY_CHANGED, SYSTEM_INSUFFICIENT_DISK_SPACE } from "../../../../constants/error-messages.js";
import { GlobalConfig } from "../../../../config/global.js";
import { logger, removeMeta } from "../../../../logger/index.js";
import { toMs } from "../../../../util/pretty-time.js";
import { compile } from "../../../../util/template/index.js";
import { ExternalHostError } from "../../../../types/errors/external-host-error.js";
import { coerceNumber } from "../../../../util/number.js";
import { getCount, isLimitReached } from "../../../global/limits.js";
import { getElapsedMs } from "../../../../util/date.js";
import { emojify } from "../../../../util/emoji.js";
import { scm } from "../../../../modules/platform/scm.js";
import { isScheduledNow } from "./schedule.js";
import { platform } from "../../../../modules/platform/index.js";
import { ensureComment, ensureCommentRemoval } from "../../../../modules/platform/comment.js";
import { embedChangelogs } from "../../changelog/index.js";
import { getMergeConfidenceLevel, isActiveConfidenceLevel, satisfiesConfidenceLevel } from "../../../../util/merge-confidence/index.js";
import { setConfidence, setStability } from "./status-checks.js";
import { ensurePr, getPlatformPrOptions } from "../pr/index.js";
import { getAdditionalFiles } from "../../../../modules/manager/npm/post-update/index.js";
import { checkAutoMerge } from "../pr/automerge.js";
import { setArtifactErrorStatus } from "./artifacts.js";
import { tryBranchAutomerge } from "./automerge.js";
import { bumpVersions } from "./bump-versions.js";
import { prAlreadyExisted } from "./check-existing.js";
import { commitFilesToBranch } from "./commit.js";
import executePostUpgradeCommands from "./execute-post-upgrade-commands.js";
import { getUpdatedPackageFiles } from "./get-updated.js";
import { handleClosedPr, handleModifiedPr } from "./handle-existing.js";
import { shouldReuseExistingBranch } from "./reuse.js";
import { isNonEmptyString } from "@sindresorhus/is";
import { DateTime } from "luxon";
//#region lib/workers/repository/update/branch/index.ts
async function setBranchStatusChecks(config) {
	await setArtifactErrorStatus(config);
	await setStability(config);
	await setConfidence(config);
}
async function rebaseCheck(config, branchPr) {
	if (branchPr.title?.startsWith("rebase!")) {
		logger.debug(`Manual rebase requested via PR title for #${branchPr.number}`);
		return true;
	}
	if (!!branchPr.labels?.includes(config.rebaseLabel)) {
		logger.debug(`Manual rebase requested via PR labels for #${branchPr.number}`);
		/* v8 ignore next -- needs test */
		if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would delete label ${config.rebaseLabel} from #${branchPr.number}`);
		else await platform.deleteLabel(branchPr.number, config.rebaseLabel);
		return true;
	}
	if (!!branchPr.bodyStruct?.rebaseRequested) {
		logger.debug(`Manual rebase requested via PR checkbox for #${branchPr.number}`);
		return true;
	}
	return false;
}
async function deleteBranchSilently(branchName) {
	try {
		await scm.deleteBranch(branchName);
	} catch (err) {
		/* v8 ignore next -- needs test */
		logger.debug({
			branchName,
			err
		}, "Branch auto-remove failed");
	}
}
function userChangedTargetBranch(pr) {
	const oldTargetBranch = pr.bodyStruct?.debugData?.targetBranch;
	if (oldTargetBranch && pr.targetBranch) return pr.targetBranch !== oldTargetBranch;
	return false;
}
async function processBranch(branchConfig, forceRebase = false) {
	let commitSha = null;
	let config = { ...branchConfig };
	logger.trace({ config }, "processBranch()");
	let branchExists = await scm.branchExists(config.branchName);
	const dependencyDashboardCheck = config.dependencyDashboardChecks?.[config.branchName];
	let updatesVerified = false;
	if (!branchExists && config.branchPrefix !== config.branchPrefixOld) {
		const branchName = config.branchName.replace(config.branchPrefix, config.branchPrefixOld);
		branchExists = await scm.branchExists(branchName);
		if (branchExists) {
			config.branchName = branchName;
			logger.debug("Found existing branch with branchPrefixOld");
		}
	}
	if (!branchExists && branchConfig.minimumGroupSize && branchConfig.minimumGroupSize > branchConfig.upgrades.length && !dependencyDashboardCheck) {
		logger.debug(`Skipping branch creation as minimumGroupSize: ${branchConfig.minimumGroupSize} is not met`);
		return {
			branchExists: false,
			result: "minimum-group-size-not-met"
		};
	}
	let branchPr = await platform.getBranchPr(config.branchName, config.baseBranch);
	logger.debug(`branchExists=${branchExists}`);
	logger.debug(`dependencyDashboardCheck=${dependencyDashboardCheck}`);
	if (branchPr) {
		config.rebaseRequested = await rebaseCheck(config, branchPr);
		logger.debug(`PR rebase requested=${config.rebaseRequested}`);
	}
	const keepUpdatedLabel = config.keepUpdatedLabel;
	const artifactErrorTopic = emojify(":warning: Artifact update problem");
	const artifactNoticeTopic = emojify(":information_source: Artifact update notice");
	try {
		const existingPr = !branchPr || config.automerge ? await prAlreadyExisted(config) : void 0;
		if (existingPr?.state === "merged") {
			logger.debug(`Matching PR #${existingPr.number} was merged previously`);
			if (config.automerge) {
				logger.debug("Disabling automerge because PR was merged previously");
				config.automerge = false;
				config.automergedPreviously = true;
			}
		} else if (!branchPr && existingPr && !dependencyDashboardCheck) {
			logger.debug({ prTitle: config.prTitle }, `Closed PR #${existingPr.number} already exists. Skipping branch.`);
			await handleClosedPr(config, existingPr);
			return {
				branchExists: false,
				prNo: existingPr.number,
				result: "already-existed"
			};
		}
		if (!branchExists && branchConfig.pendingChecks && !dependencyDashboardCheck) {
			logger.debug(`Branch ${config.branchName} creation is disabled because internalChecksFilter was not met`);
			return {
				branchExists: false,
				result: "pending"
			};
		}
		if (!branchExists) {
			if (config.mode === "silent" && !dependencyDashboardCheck) {
				logger.debug(`Branch ${config.branchName} creation is disabled because mode=silent`);
				return {
					branchExists,
					result: "needs-approval"
				};
			}
			if (config.dependencyDashboardApproval && !dependencyDashboardCheck) {
				logger.debug(`Branch ${config.branchName} creation is disabled because dependencyDashboardApproval=true`);
				return {
					branchExists,
					result: "needs-approval"
				};
			}
		}
		logger.debug(`Open PR Count: ${getCount("ConcurrentPRs")}, Existing Branch Count: ${getCount("Branches")}, Hourly PR Count: ${getCount("HourlyPRs")}, Hourly Commit Count: ${getCount("HourlyCommits")}`);
		if (!branchExists && isLimitReached("Branches", branchConfig) && !dependencyDashboardCheck && !config.isVulnerabilityAlert) {
			logger.debug("Reached branch limit - skipping branch creation");
			return {
				branchExists,
				result: "branch-limit-reached"
			};
		}
		if (!branchConfig.rebaseRequested && isLimitReached("Commits") && !dependencyDashboardCheck && !config.isVulnerabilityAlert) {
			logger.debug("Reached commits per run limit - skipping branch");
			return {
				branchExists,
				prNo: branchPr?.number,
				result: "commit-per-run-limit-reached"
			};
		}
		if (!branchConfig.rebaseRequested && isLimitReached("HourlyCommits", branchConfig) && !dependencyDashboardCheck && !config.isVulnerabilityAlert) {
			logger.debug("Reached hourly commits limit - skipping branch");
			return {
				branchExists,
				prNo: branchPr?.number,
				result: "commit-hourly-limit-reached"
			};
		}
		if (branchExists) {
			config.stopUpdating = branchPr?.labels?.includes(config.stopUpdatingLabel);
			const prRebaseChecked = !!branchPr?.bodyStruct?.rebaseRequested;
			if (branchExists && !dependencyDashboardCheck && !prRebaseChecked) {
				if (config.stopUpdating) {
					logger.info("Branch updating is skipped because stopUpdatingLabel is present in config");
					return {
						branchExists: true,
						prNo: branchPr?.number,
						result: "no-work"
					};
				}
				if (config.pendingChecks) {
					logger.info("Branch updating is skipped because internalChecksFilter was not met");
					return {
						branchExists: true,
						prNo: branchPr?.number,
						result: "pending"
					};
				}
			}
			logger.debug("Checking if PR has been edited");
			const branchIsModified = await scm.isBranchModified(config.branchName, config.baseBranch);
			if (branchPr) {
				logger.debug(`Found existing branch PR #${branchPr.number}`);
				if (branchPr.state !== "open") {
					logger.debug("PR has been closed or merged since this run started - aborting");
					throw new Error(REPOSITORY_CHANGED);
				}
				if (branchIsModified || userChangedTargetBranch(branchPr)) {
					logger.debug(`PR has been edited, PrNo:${branchPr.number}`);
					await handleModifiedPr(config, branchPr);
					if (!(!!dependencyDashboardCheck || config.rebaseRequested)) return {
						branchExists,
						prNo: branchPr.number,
						result: "pr-edited"
					};
				}
			} else if (branchIsModified) {
				const oldPr = await platform.findPr({
					branchName: config.branchName,
					state: "!open",
					targetBranch: config.baseBranch
				});
				if (!oldPr) {
					logger.debug("Branch has been edited but found no PR - skipping");
					return {
						branchExists,
						result: "pr-edited"
					};
				}
				const branchSha = await scm.getBranchCommit(config.branchName);
				const oldPrSha = oldPr?.sha;
				if (!oldPrSha || oldPrSha === branchSha) logger.debug({
					oldPrNumber: oldPr.number,
					oldPrSha,
					branchSha
				}, "Found old PR matching this branch - will override it");
				else {
					logger.debug({
						oldPrNumber: oldPr.number,
						oldPrSha,
						branchSha
					}, "Found old PR but the SHA is different");
					return {
						branchExists,
						result: "pr-edited"
					};
				}
			}
		}
		config.isScheduledNow = isScheduledNow(config, "schedule");
		if (!config.isScheduledNow && !dependencyDashboardCheck) {
			if (!branchExists) {
				logger.debug("Skipping branch creation as not within schedule");
				return {
					branchExists,
					result: "not-scheduled"
				};
			}
			if (config.updateNotScheduled === false && !config.rebaseRequested) {
				logger.debug("Skipping branch update as not within schedule");
				return {
					branchExists,
					prNo: branchPr?.number,
					result: "update-not-scheduled"
				};
			}
			if (!branchPr && !(config.automerge && config.automergeType === "branch")) {
				logger.debug("Skipping PR creation out of schedule");
				return {
					branchExists,
					result: "not-scheduled"
				};
			}
			logger.debug("Branch + PR exists but is not scheduled -- will update if necessary");
		}
		if (config.upgrades.some((upgrade) => isNonEmptyString(upgrade.minimumReleaseAge) || isActiveConfidenceLevel(upgrade.minimumConfidence))) {
			const depNamesWithoutReleaseTimestamp = {
				"timestamp-required": [],
				"timestamp-optional": []
			};
			config.stabilityStatus = "green";
			for (const upgrade of config.upgrades) {
				const minimumReleaseAgeMs = isNonEmptyString(upgrade.minimumReleaseAge) ? coerceNumber(toMs(upgrade.minimumReleaseAge), 0) : 0;
				if (minimumReleaseAgeMs) {
					const minimumReleaseAgeBehaviour = upgrade.minimumReleaseAgeBehaviour ?? "timestamp-required";
					if (upgrade.releaseTimestamp) {
						const timeElapsed = getElapsedMs(upgrade.releaseTimestamp);
						if (timeElapsed < minimumReleaseAgeMs) {
							logger.debug({
								depName: upgrade.depName,
								timeElapsed,
								minimumReleaseAge: upgrade.minimumReleaseAge
							}, "Update has not passed minimum release age");
							config.stabilityStatus = "yellow";
							continue;
						}
					} else if (minimumReleaseAgeBehaviour === "timestamp-required") {
						depNamesWithoutReleaseTimestamp["timestamp-required"].push({
							depName: upgrade.depName,
							updateType: upgrade.updateType
						});
						config.stabilityStatus = "yellow";
						continue;
					} else depNamesWithoutReleaseTimestamp["timestamp-optional"].push({
						depName: upgrade.depName,
						updateType: upgrade.updateType
					});
				}
				const datasource = upgrade.datasource;
				const depName = upgrade.depName;
				const packageName = upgrade.packageName;
				const minimumConfidence = upgrade.minimumConfidence;
				const updateType = upgrade.updateType;
				const currentVersion = upgrade.currentVersion;
				const newVersion = upgrade.newVersion;
				if (isActiveConfidenceLevel(minimumConfidence)) {
					const confidence = await getMergeConfidenceLevel(datasource, packageName, currentVersion, newVersion, updateType) ?? "neutral";
					if (satisfiesConfidenceLevel(confidence, minimumConfidence)) config.confidenceStatus = "green";
					else {
						logger.debug({
							depName,
							confidence,
							minimumConfidence
						}, "Update does not meet minimum confidence scores");
						config.confidenceStatus = "yellow";
						continue;
					}
				}
			}
			if (depNamesWithoutReleaseTimestamp["timestamp-required"].length) logger.once.debug({ updates: depNamesWithoutReleaseTimestamp["timestamp-required"] }, `Marking ${depNamesWithoutReleaseTimestamp["timestamp-required"].length} release(s) as pending, as they do not have a releaseTimestamp and we're running with minimumReleaseAgeBehaviour=timestamp-required`);
			if (depNamesWithoutReleaseTimestamp["timestamp-optional"].length) {
				logger.once.warn("Some upgrade(s) did not have a releaseTimestamp, but as we're running with minimumReleaseAgeBehaviour=timestamp-optional, proceeding. See debug logs for more information");
				logger.once.debug({ updates: depNamesWithoutReleaseTimestamp["timestamp-optional"] }, `${depNamesWithoutReleaseTimestamp["timestamp-optional"].length} upgrade(s) did not have a releaseTimestamp, but as we're running with minimumReleaseAgeBehaviour=timestamp-optional, proceeding`);
			}
			if (!dependencyDashboardCheck && !branchExists && config.stabilityStatus === "yellow" && ["not-pending", "status-success"].includes(config.prCreation)) {
				logger.debug("Skipping branch creation due to internal status checks not met");
				return {
					branchExists,
					result: "pending"
				};
			}
		}
		let userRebaseRequested = dependencyDashboardCheck === "rebase" || !!config.dependencyDashboardRebaseAllOpen || !!config.rebaseRequested;
		const userApproveAllPendingPR = !!config.dependencyDashboardAllPending;
		const userOpenAllRateLimtedPR = !!config.dependencyDashboardAllRateLimited;
		const userOpenAllSchedulePendingPR = !!config.dependencyDashboardAllAwaitingSchedule;
		if (forceRebase) {
			logger.debug("Force rebase because branch needs updating");
			config.reuseExistingBranch = false;
		} else if (userRebaseRequested) {
			logger.debug("User has requested rebase");
			config.reuseExistingBranch = false;
		} else if (dependencyDashboardCheck === "global-config") {
			logger.debug(`Manual create/rebase requested via checkedBranches`);
			config.reuseExistingBranch = false;
			userRebaseRequested = true;
		} else if (userApproveAllPendingPR) logger.debug("A user manually approved all pending PRs via the Dependency Dashboard.");
		else if (userOpenAllRateLimtedPR) logger.debug("A user manually approved all rate-limited PRs via the Dependency Dashboard.");
		else if (userOpenAllSchedulePendingPR) logger.debug("A user manually requested all awaiting schedule PRs via the Dependency Dashboard.");
		else if (branchExists && config.rebaseWhen === "never" && !(keepUpdatedLabel && branchPr?.labels?.includes(keepUpdatedLabel)) && !dependencyDashboardCheck) {
			logger.debug("rebaseWhen=never so skipping branch update check");
			return {
				branchExists,
				prNo: branchPr?.number,
				result: "no-work"
			};
		} else if (branchPr?.targetBranch && branchPr.targetBranch !== config.baseBranch) {
			logger.debug("Base branch changed by user, rebasing the branch onto new base");
			config.reuseExistingBranch = false;
		} else if (config.cacheFingerprintMatch === "no-match") {
			logger.debug("Cache fingerprint does not match, cannot reuse existing branch");
			config.reuseExistingBranch = false;
		} else config = await shouldReuseExistingBranch(config);
		logger.debug(`Using reuseExistingBranch: ${config.reuseExistingBranch}`);
		if (!(config.reuseExistingBranch && config.cacheFingerprintMatch === "matched")) {
			await scm.checkoutBranch(config.baseBranch);
			const res = await getUpdatedPackageFiles(config);
			if (res.artifactErrors && config.artifactErrors) res.artifactErrors = config.artifactErrors.concat(res.artifactErrors);
			config = {
				...config,
				...res
			};
			if (config.updatedPackageFiles?.length) {
				logger.debug(`Updated ${config.updatedPackageFiles.length} package files`);
				if (config.reuseExistingBranch && !forceRebase) {
					logger.debug("Existing branch needs updating. Restarting processBranch() with a clean branch");
					return processBranch(branchConfig, true);
				}
			} else logger.debug("No package files need updating");
			const additionalFiles = await getAdditionalFiles(config, branchConfig.packageFiles);
			config.artifactErrors = (config.artifactErrors ?? []).concat(additionalFiles.artifactErrors);
			config.updatedArtifacts = (config.updatedArtifacts ?? []).concat(additionalFiles.updatedArtifacts);
			if (config.updatedArtifacts?.length) {
				logger.debug({ updatedArtifacts: config.updatedArtifacts.map((f) => f.type === "deletion" ? `${f.path} (delete)` : f.path) }, `Updated ${config.updatedArtifacts.length} lock files`);
				if (config.reuseExistingBranch && !forceRebase) {
					logger.debug("Existing branch needs updating. Restarting processBranch() with a clean branch");
					return processBranch(branchConfig, true);
				}
			} else logger.debug("No updated lock files in branch");
			await embedChangelogs({
				upgrades: config.upgrades,
				stage: "branch"
			});
			const postUpgradeCommandResults = await executePostUpgradeCommands(config);
			if (postUpgradeCommandResults !== null) {
				const { updatedArtifacts, artifactErrors } = postUpgradeCommandResults;
				config.updatedArtifacts = updatedArtifacts;
				config.artifactErrors = artifactErrors;
			}
			await bumpVersions(config);
			removeMeta(["dep"]);
			if (config.artifactErrors?.length) if (config.releaseTimestamp) {
				logger.debug(`Branch timestamp: ` + config.releaseTimestamp);
				if (DateTime.fromISO(config.releaseTimestamp).plus({ hours: 2 }) < DateTime.local()) logger.debug("PR is older than 2 hours, raise PR with lock file errors");
				else if (branchExists) logger.debug("PR is less than 2 hours old but branchExists so updating anyway");
				else {
					logger.debug("PR is less than 2 hours old - raise error instead of PR");
					throw new Error(MANAGER_LOCKFILE_ERROR);
				}
			} else logger.debug("PR has no releaseTimestamp");
			else if (config.updatedArtifacts?.length && branchPr) if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would ensure comment removal in PR #${branchPr.number}`);
			else {
				await ensureCommentRemoval({
					type: "by-topic",
					number: branchPr.number,
					topic: artifactErrorTopic
				});
				if (!config.artifactNotices?.length) await ensureCommentRemoval({
					type: "by-topic",
					number: branchPr.number,
					topic: artifactNoticeTopic
				});
			}
			const forcedManually = userRebaseRequested || !branchExists;
			config.isConflicted ??= branchExists && await scm.isBranchConflicted(config.baseBranch, config.branchName);
			config.forceCommit = forcedManually || config.isConflicted;
			if (config.commitBody) {
				config.commitMessage = `${config.commitMessage}\n\n${compile(config.commitBody, {
					...config,
					logJSON: config.upgrades[0].logJSON,
					releases: config.upgrades[0].releases
				})}`;
				logger.trace(`commitMessage: ` + JSON.stringify(config.commitMessage));
			}
			commitSha = await commitFilesToBranch(config);
			await scm.checkoutBranch(config.baseBranch);
			updatesVerified = true;
		}
		if (branchPr) {
			const platformPrOptions = getPlatformPrOptions(config);
			if (commitSha && platformPrOptions.usePlatformAutomerge && platform.reattemptPlatformAutomerge) if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would reattempt platform automerge for PR #${branchPr.number}`);
			else await platform.reattemptPlatformAutomerge({
				number: branchPr.number,
				platformPrOptions
			});
			if (platform.refreshPr) await platform.refreshPr(branchPr.number);
		}
		if (!commitSha && !branchExists) return {
			branchExists,
			result: "no-work"
		};
		if (commitSha) {
			const action = branchExists ? "updated" : "created";
			logger.info({ commitSha }, `Branch ${action}`);
		}
		await setBranchStatusChecks(config);
		if (!branchPr && !config.artifactErrors?.length && !userRebaseRequested && commitSha && config.prCreation !== "immediate") {
			logger.debug(`Branch status pending, current sha: ${commitSha}`);
			return {
				branchExists: true,
				updatesVerified,
				result: "pending",
				commitSha
			};
		}
		if (!config.artifactErrors?.length && (!commitSha || config.ignoreTests)) {
			const mergeStatus = await tryBranchAutomerge(config);
			logger.debug(`mergeStatus=${mergeStatus}`);
			if (mergeStatus === "automerged") {
				if (GlobalConfig.get("dryRun")) logger.info("DRY-RUN: Would delete branch" + config.branchName);
				else await deleteBranchSilently(config.branchName);
				logger.debug("Branch is automerged - returning");
				return {
					branchExists: false,
					result: "automerged"
				};
			}
			if (mergeStatus === "off schedule") if (userRebaseRequested) config.forcePr = true;
			else {
				logger.debug("Branch cannot automerge now because automergeSchedule is off schedule - skipping");
				return {
					branchExists,
					result: "not-scheduled",
					commitSha
				};
			}
			if (mergeStatus === "stale" && ["conflicted", "never"].includes(config.rebaseWhen) && !(keepUpdatedLabel && branchPr?.labels?.includes(keepUpdatedLabel))) {
				logger.warn("Branch cannot automerge because it is behind base branch and rebaseWhen setting disallows rebasing - raising a PR instead");
				config.forcePr = true;
				config.branchAutomergeFailureMessage = mergeStatus;
			}
			if (mergeStatus === "automerge aborted - PR exists" || mergeStatus === "branch status error" || mergeStatus === "failed") {
				logger.debug(`Branch automerge not possible, mergeStatus:${mergeStatus}`);
				config.forcePr = true;
				config.branchAutomergeFailureMessage = mergeStatus;
			}
		}
	} catch (err) {
		/* v8 ignore if -- needs test */
		if (err.statusCode === 404) {
			logger.debug({ err }, "Received a 404 error - aborting run");
			throw new Error(REPOSITORY_CHANGED);
		}
		/* v8 ignore if -- needs test */
		if (err.message === "rate-limit-exceeded") {
			logger.debug("Passing rate-limit-exceeded error up");
			throw err;
		}
		if (err.message === "repository-changed") {
			logger.debug("Passing repository-changed error up");
			throw err;
		}
		/* v8 ignore if -- needs test */
		if (err.message?.startsWith("remote: Invalid username or password")) {
			logger.debug("Throwing bad credentials");
			throw new Error(PLATFORM_BAD_CREDENTIALS);
		}
		/* v8 ignore if -- needs test */
		if (err.message?.startsWith("ssh_exchange_identification: Connection closed by remote host")) {
			logger.debug("Throwing bad credentials");
			throw new Error(PLATFORM_BAD_CREDENTIALS);
		}
		/* v8 ignore if -- needs test */
		if (err.message === "bad-credentials") {
			logger.debug("Passing bad-credentials error up");
			throw err;
		}
		/* v8 ignore if -- needs test */
		if (err.message === "integration-unauthorized") {
			logger.debug("Passing integration-unauthorized error up");
			throw err;
		}
		if (err.message === "lockfile-error") {
			logger.debug("Passing lockfile-error up");
			throw err;
		}
		/* v8 ignore if -- needs test */
		if (err.message?.includes("space left on device")) throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE);
		/* v8 ignore if -- needs test */
		if (err.message === "disk-space") {
			logger.debug("Passing disk-space error up");
			throw err;
		}
		/* v8 ignore if -- needs test */
		if (err.message.startsWith("Resource not accessible by integration")) {
			logger.debug("Passing 403 error up");
			throw err;
		}
		/* v8 ignore next -- needs test */
		if (err.message === "update-failure") logger.warn("Error updating branch: update failure");
		else if (err.message.startsWith("bundler-")) return {
			branchExists: true,
			updatesVerified,
			prNo: branchPr?.number,
			result: "error",
			commitSha
		};
		else if (err.message?.includes("fatal: Authentication failed")) throw new Error(PLATFORM_AUTHENTICATION_ERROR);
		else if (err.message?.includes("fatal: bad revision")) {
			logger.debug({ err }, "Aborting job due to bad revision error");
			throw new Error(REPOSITORY_CHANGED);
		} else if (err.message === "config-validation") {
			logger.debug("Passing config validation error up");
			throw err;
		} else if (err.message === "temporary-error") {
			logger.debug("Passing TEMPORARY_ERROR error up");
			throw err;
		} else if (!(err instanceof ExternalHostError)) logger.warn({ err }, `Error updating branch`);
		return {
			branchExists,
			prNo: branchPr?.number,
			result: "error",
			commitSha
		};
	}
	try {
		logger.debug("Ensuring PR");
		logger.debug(`There are ${config.errors.length} errors and ${config.warnings.length} warnings`);
		const ensurePrResult = await ensurePr(config);
		if (ensurePrResult.type === "without-pr") {
			const { prBlockedBy } = ensurePrResult;
			branchPr = null;
			if (prBlockedBy === "RateLimited" && !config.isVulnerabilityAlert) {
				logger.debug("Reached PR limit - skipping PR creation");
				return {
					branchExists,
					prBlockedBy,
					result: "pr-limit-reached",
					commitSha
				};
			}
			if (prBlockedBy === "NeedsApproval") return {
				branchExists,
				prBlockedBy,
				result: "needs-pr-approval",
				commitSha
			};
			if (prBlockedBy === "AwaitingTests") return {
				branchExists,
				prBlockedBy,
				result: "pending",
				commitSha
			};
			if (prBlockedBy === "BranchAutomerge") return {
				branchExists,
				prBlockedBy,
				result: "done",
				commitSha
			};
			if (prBlockedBy === "Error") return {
				branchExists,
				prBlockedBy,
				result: "error",
				commitSha
			};
			logger.warn({ prBlockedBy }, "Unknown PrBlockedBy result");
			return {
				branchExists,
				prBlockedBy,
				result: "error",
				commitSha
			};
		}
		if (ensurePrResult.type === "with-pr") {
			const { pr } = ensurePrResult;
			branchPr = pr;
			await setBranchStatusChecks(config);
			if (config.artifactErrors?.length) {
				logger.warn({ artifactErrors: config.artifactErrors }, "artifactErrors");
				let content = `Renovate failed to update `;
				content += config.artifactErrors.length > 1 ? "artifacts" : "an artifact";
				content += " related to this branch. ";
				content += compile(config.userStrings.artifactErrorWarning, config);
				content += emojify(`\n\n:recycle: Renovate will retry this branch, including artifacts, only when one of the following happens:\n\n`);
				content += " - any of the package files in this branch needs updating, or \n";
				content += " - the branch becomes conflicted, or\n";
				content += " - you click the rebase/retry checkbox if found above, or\n";
				content += " - you rename this PR's title to start with \"rebase!\" to trigger it manually";
				content += "\n\nThe artifact failure details are included below:\n\n";
				config.artifactErrors.forEach((error) => {
					content += `##### File name: ${error.fileName}\n\n`;
					content += `\`\`\`\n${error.stderr}\n\`\`\`\n\n`;
				});
				content = platform.massageMarkdown(content, config.rebaseLabel);
				if (!(config.suppressNotifications.includes("artifactErrors") || config.suppressNotifications.includes("lockFileErrors"))) if (GlobalConfig.get("dryRun")) logger.info(`DRY-RUN: Would ensure lock file error comment in PR #${pr.number}`);
				else await ensureComment({
					number: pr.number,
					topic: artifactErrorTopic,
					content
				});
			} else {
				if (config.artifactNotices?.length) {
					const contentLines = [];
					for (const notice of config.artifactNotices) {
						contentLines.push(`##### File name: ${notice.file}`);
						contentLines.push(notice.message);
					}
					const content = contentLines.join("\n\n");
					await ensureComment({
						number: pr.number,
						topic: artifactNoticeTopic,
						content
					});
				}
				if (config.automerge) {
					logger.debug("PR is configured for automerge");
					if (config.ignoreTests === true || !commitSha) {
						logger.debug("checking auto-merge");
						if ((await checkAutoMerge(pr, config))?.automerged) return {
							branchExists,
							result: "automerged",
							commitSha
						};
					}
				} else logger.debug("PR is not configured for automerge");
			}
		}
	} catch (err) {
		/* v8 ignore if -- needs test */
		if (err instanceof ExternalHostError || ["rate-limit-exceeded", "repository-changed"].includes(err.message)) {
			logger.debug("Passing PR error up");
			throw err;
		}
		logger.error({ err }, `Error ensuring PR`);
	}
	if (!branchExists) return {
		branchExists: true,
		updatesVerified,
		prNo: branchPr?.number,
		result: "pr-created",
		commitSha
	};
	return {
		branchExists,
		updatesVerified,
		prNo: branchPr?.number,
		result: "done",
		commitSha
	};
}
//#endregion
export { processBranch };

//# sourceMappingURL=index.js.map