import { regEx } from "../../../../util/regex.js";
import { GlobalConfig } from "../../../../config/global.js";
import { logger } from "../../../../logger/index.js";
import { UpdateTypesOptions } from "../../../../config/types.js";
import { coerceNumber } from "../../../../util/number.js";
import { emojify } from "../../../../util/emoji.js";
//#region lib/workers/repository/onboarding/pr/pr-list.ts
/** Priority order when rendering the `SummaryCategory`s in the summary view.
*
* NOTE that this is not exhaustive - this is only the priority order for rendering, and other categories, such as `other` are placed after.
*/
const SUMMARY_CATEGORY_PRIORITY_ORDER = ["security", ...UpdateTypesOptions];
function getExpectedPrList(config, branches) {
	logger.debug("getExpectedPrList()");
	logger.trace({ config });
	let prDesc = `\n### What to Expect\n\n`;
	if (!branches.length) return `${prDesc}It looks like your repository dependencies are already up-to-date and no Pull Requests will be necessary right away.\n`;
	prDesc += `With your current configuration, Renovate will create ${branches.length} Pull Request`;
	prDesc += branches.length > 1 ? `s:\n\n` : `:\n\n`;
	for (const branch of branches) {
		const prTitleRe = regEx(/@([a-z]+\/[a-z]+)/);
		prDesc += `<details>\n<summary>${branch.prTitle.replace(prTitleRe, "@&#8203;$1")}</summary>\n\n`;
		if (branch.schedule?.length) prDesc += `  - Schedule: ${JSON.stringify(branch.schedule)}\n`;
		prDesc += `  - Branch name: \`${branch.branchName}\`\n`;
		prDesc += branch.baseBranch ? `  - Merge into: \`${branch.baseBranch}\`\n` : "";
		const seen = [];
		for (const upgrade of branch.upgrades) {
			let text = "";
			if (upgrade.updateType === "lockFileMaintenance") text += "  - Regenerate lock files to use latest dependency versions";
			else {
				if (upgrade.updateType === "pin") text += "  - Pin ";
				else text += "  - Upgrade ";
				if (upgrade.sourceUrl) text += `[${upgrade.depName}](${upgrade.sourceUrl})`;
				else text += upgrade.depName.replace(prTitleRe, "@&#8203;$1");
				text += upgrade.isLockfileUpdate ? ` to \`${upgrade.newVersion}\`` : ` to \`${upgrade.newDigest ?? upgrade.newValue}\``;
				text += "\n";
			}
			if (!seen.includes(text)) {
				prDesc += text;
				seen.push(text);
			}
		}
		prDesc += "\n\n";
		prDesc += "</details>\n\n";
	}
	const prHourlyLimit = coerceNumber(config.prHourlyLimit);
	const commitHourlyLimit = coerceNumber(config.commitHourlyLimit);
	if (commitHourlyLimit > 0 && commitHourlyLimit < 5 && commitHourlyLimit < branches.length) prDesc += emojify(`\n\n:children_crossing: Branch creation and rebasing will be limited to maximum ${commitHourlyLimit} per hour, so it doesn't swamp any CI resources or overwhelm the project. See [docs for \`commitHourlyLimit\`](${GlobalConfig.get("productLinks").documentation}configuration-options/#commithourlylimit) for details.\n\n`);
	else if (prHourlyLimit > 0 && prHourlyLimit < 5 && prHourlyLimit < branches.length) prDesc += emojify(`\n\n:children_crossing: PR creation will be limited to maximum ${prHourlyLimit} per hour, so it doesn't swamp any CI resources or overwhelm the project. See [docs for \`prHourlyLimit\`](${GlobalConfig.get("productLinks").documentation}configuration-options/#prhourlylimit) for details.\n\n`);
	return prDesc;
}
function getBranchUpgradeTypes(branch) {
	if (branch.isVulnerabilityAlert) return /* @__PURE__ */ new Set(["security"]);
	const types = /* @__PURE__ */ new Set();
	for (const upgrade of branch.upgrades) if (upgrade.updateType) types.add(upgrade.updateType);
	else if (upgrade.isLockfileUpdate) types.add("lockfileUpdate");
	else types.add("other");
	return types;
}
function sortBaseBranches(bases) {
	return [...bases].sort((a, b) => {
		if (a === b)
 /* v8 ignore next -- base branches are unique Record keys; equal comparison cannot occur */
		return 0;
		if (a === "") return -1;
		if (b === "") return 1;
		return a.localeCompare(b);
	});
}
function describeSecurityGroup(groupBranches) {
	const firstUpgrade = groupBranches[0].upgrades[0];
	const depName = firstUpgrade?.depName ?? groupBranches[0].prTitle ?? "";
	const updateType = firstUpgrade?.updateType ?? "unknown";
	const packageFiles = groupBranches.map((b) => ({
		file: b.packageFile ?? "",
		manager: b.manager
	})).filter((f) => f.file);
	const uniqueManagers = new Set(packageFiles.map((f) => f.manager));
	if (packageFiles.length === 0) {
		const branchManagers = new Set(groupBranches.map((b) => b.manager));
		if (branchManagers.size === 1) return `- \`${depName}\`, (${[...branchManagers][0]}, ${updateType})\n`;
		return `- \`${depName}\`, (${updateType}): ${[...branchManagers].sort().join(", ")}\n`;
	}
	if (packageFiles.length === 1) {
		const file = packageFiles[0].file;
		const [manager] = uniqueManagers;
		return `- \`${depName}\`, (${manager}, ${updateType}): \`${file}\`\n`;
	}
	if (uniqueManagers.size === 1) return `- \`${depName}\`, (${[...uniqueManagers][0]}, ${updateType}):\n${packageFiles.map(({ file }) => `  - \`${file}\`\n`).join("")}`;
	return `- \`${depName}\`, (${updateType}):\n${packageFiles.map(({ file, manager }) => `  - \`${file}\` (${manager})\n`).join("")}`;
}
function collectBranchStats(branches) {
	const prCountByBase = {};
	const tableStats = {};
	const presentTypes = /* @__PURE__ */ new Set();
	const securityGroups = {};
	const seenPrs = /* @__PURE__ */ new Set();
	const seenTableKeys = /* @__PURE__ */ new Set();
	for (const branch of branches) {
		const base = branch.baseBranch ?? "";
		const { manager, branchName } = branch;
		const branchTypes = getBranchUpgradeTypes(branch);
		for (const type of branchTypes) presentTypes.add(type);
		if (!seenPrs.has(branchName)) {
			seenPrs.add(branchName);
			prCountByBase[base] = (prCountByBase[base] ?? 0) + 1;
		}
		tableStats[base] ??= {};
		const baseStats = tableStats[base];
		baseStats[manager] ??= {};
		const managerStats = baseStats[manager];
		for (const type of branchTypes) {
			const key = `${branchName}:${manager}:${type}`;
			if (seenTableKeys.has(key)) continue;
			seenTableKeys.add(key);
			managerStats[type] ??= 0;
			managerStats[type]++;
		}
		if (branch.isVulnerabilityAlert) {
			securityGroups[branchName] ??= [];
			securityGroups[branchName].push(branch);
		}
	}
	return {
		prCountByBase,
		tableStats,
		presentCategories: presentTypes,
		securityGroups,
		prCount: seenPrs.size
	};
}
function getCategoryColumns(presentTypes) {
	const cols = SUMMARY_CATEGORY_PRIORITY_ORDER.filter((t) => presentTypes.has(t));
	for (const t of presentTypes) if (t !== "other" && !cols.includes(t)) cols.push(t);
	if (presentTypes.has("other")) cols.push("other");
	return cols;
}
function categoriesToColumnHeaders(cols) {
	/* v8 ignore next -- branches always have at least one upgrade, so cols is never empty in practice */
	if (cols.length === 0) return "";
	return ` | ${cols.join(" | ")}`;
}
function renderRowCounts(typeColumns, typeCounts) {
	return categoriesToColumnHeaders(typeColumns.map((t) => `${typeCounts[t] ?? 0}`));
}
function sumCategoryCounts(rows, categoryColumns) {
	const sum = {};
	for (const categoryCounts of rows) for (const category of categoryColumns) sum[category] = (sum[category] ?? 0) + (categoryCounts[category] ?? 0);
	return sum;
}
function getExpectedPrListSummary(config, branches) {
	logger.debug("getExpectedPrListSummary()");
	logger.trace({ config });
	let prDesc = `\n### What to Expect\n\n`;
	if (!branches.length) return `${prDesc}It looks like your repository dependencies are already up-to-date and no Pull Requests will be necessary right away.\n`;
	const stats = collectBranchStats(branches);
	const sortedBases = sortBaseBranches(Object.keys(stats.prCountByBase));
	const hasMultipleBaseBranches = sortedBases.length > 1;
	const prHourlyLimit = coerceNumber(config.prHourlyLimit);
	const commitHourlyLimit = coerceNumber(config.commitHourlyLimit);
	if (hasMultipleBaseBranches) {
		let total = 0;
		const parts = sortedBases.map((base) => {
			const count = stats.prCountByBase[base];
			total += count;
			const label = base ? `the \`${base}\` branch` : "the default branch";
			return `${count} Pull Request${count > 1 ? "s" : ""} to ${label}`;
		});
		const hourlyLimitsNotice = determineHourlyLimitsNotice(prHourlyLimit, commitHourlyLimit, total);
		prDesc += `With your current configuration, Renovate will create ${parts.join(" and ")}${hourlyLimitsNotice}:\n\n`;
	} else {
		const hourlyLimitsNotice = determineHourlyLimitsNotice(prHourlyLimit, commitHourlyLimit, stats.prCount);
		prDesc += `With your current configuration, Renovate will create ${stats.prCount} Pull Request${stats.prCount > 1 ? "s" : ""}${hourlyLimitsNotice}:\n\n`;
	}
	const categoryColumns = getCategoryColumns(stats.presentCategories);
	if (hasMultipleBaseBranches) {
		prDesc += `| Branch | Manager${categoriesToColumnHeaders(categoryColumns)} |\n`;
		prDesc += `| --- | ---${categoryColumns.map(() => " | ---").join("")} |\n`;
		for (const base of sortedBases) {
			const label = base || "$default";
			for (const [manager, typeCounts] of Object.entries(stats.tableStats[base])) prDesc += `| ${label} | ${manager}${renderRowCounts(categoryColumns, typeCounts)} |\n`;
		}
		const sumRow = sumCategoryCounts(Object.values(stats.tableStats).flatMap((b) => Object.values(b)), categoryColumns);
		prDesc += `| **Total** | ${renderRowCounts(categoryColumns, sumRow)} |\n`;
	} else {
		prDesc += `| Manager${categoriesToColumnHeaders(categoryColumns)} |\n`;
		prDesc += `| ---${categoryColumns.map(() => " | ---").join("")} |\n`;
		for (const [manager, typeCounts] of Object.entries(stats.tableStats[sortedBases[0]])) prDesc += `| ${manager}${renderRowCounts(categoryColumns, typeCounts)} |\n`;
		const sumRow = sumCategoryCounts(Object.values(stats.tableStats[sortedBases[0]]), categoryColumns);
		prDesc += `| **Total**${renderRowCounts(categoryColumns, sumRow)} |\n`;
	}
	prDesc += `\n<small>Note that a single PR can update multiple files and/or managers, so the above rows may not align with the number of PRs being listed above.</small>\n`;
	if (Object.keys(stats.securityGroups).length) {
		prDesc += `\n**Security updates**:\n\n`;
		for (const groupBranches of Object.values(stats.securityGroups)) prDesc += describeSecurityGroup(groupBranches);
	}
	if (commitHourlyLimit > 0 && commitHourlyLimit < 5 && commitHourlyLimit < stats.prCount) prDesc += emojify(`\n\n:children_crossing: Branch creation and rebasing will be limited to maximum ${commitHourlyLimit} per hour, so it doesn't swamp any CI resources or overwhelm the project. See [docs for \`commitHourlyLimit\`](${GlobalConfig.get("productLinks").documentation}configuration-options/#commithourlylimit) for details.\n\n`);
	else if (prHourlyLimit > 0 && prHourlyLimit < 5 && prHourlyLimit < stats.prCount) prDesc += emojify(`\n\n:children_crossing: PR creation will be limited to maximum ${prHourlyLimit} per hour, so it doesn't swamp any CI resources or overwhelm the project. See [docs for \`prHourlyLimit\`](${GlobalConfig.get("productLinks").documentation}configuration-options/#prhourlylimit) for details.\n\n`);
	return prDesc;
}
function determineHourlyLimitsNotice(prHourlyLimit, commitHourlyLimit, prCount) {
	if (commitHourlyLimit === 0 && prHourlyLimit === 0) return " (with no configured maximum of PRs per hour)";
	if (commitHourlyLimit > 0 && commitHourlyLimit < 5 && commitHourlyLimit < prCount) return emojify(` (at a maximum of ${commitHourlyLimit} PR${commitHourlyLimit > 1 ? "s" : ""}/rebase${commitHourlyLimit > 1 ? "s" : ""} per hour)`);
	if (prHourlyLimit > 0 && prHourlyLimit < 5 && prHourlyLimit < prCount) return emojify(` (at a maximum of ${prHourlyLimit} PR${prHourlyLimit > 1 ? "s" : ""} per hour)`);
	return "";
}
//#endregion
export { getExpectedPrList, getExpectedPrListSummary };

//# sourceMappingURL=pr-list.js.map