import { logger } from "../../../logger/index.js";
import { hash } from "../../../util/hash.js";
import { isLongCommitSha } from "../../../util/schema-utils/git.js";
import { branchExists, deleteBranch, fetchRevSpec, getBranchCommit, getBranchUpdateDate, hasDiff, mergeToLocal, prepareCommit, pushCommit } from "../../../util/git/index.js";
import { DefaultGitScm } from "../default-scm.js";
import { convertGerritDateToISO } from "./utils.js";
import { client } from "./client.js";
import { isNonEmptyArray } from "@sindresorhus/is";
import { randomUUID } from "node:crypto";
import { DateTime } from "luxon";
//#region lib/modules/platform/gerrit/scm.ts
let repository;
let username;
function configureScm(repo, login) {
	repository = repo;
	username = login;
}
/** Branches with a local commit but no Gerrit change yet (push deferred to createPr()). */
const pendingChangeBranches = /* @__PURE__ */ new Set();
async function pushForReview(options) {
	const pushOptions = ["notify=NONE", "ready"];
	if (options.autoApprove) pushOptions.push("label=Code-Review+2");
	if (isNonEmptyArray(options.labels)) for (const label of options.labels) pushOptions.push(`hashtag=${label}`);
	const result = await pushCommit({
		sourceRef: options.sourceRef,
		targetRef: `refs/for/${options.targetBranch}`,
		files: options.files,
		pushOptions
	});
	if (result) pendingChangeBranches.delete(options.sourceRef);
	return result;
}
var GerritScm = class extends DefaultGitScm {
	async branchExists(branchName) {
		const searchConfig = {
			state: "open",
			branchName,
			singleChange: true
		};
		if ((await client.findChanges(repository, searchConfig)).pop()) return true;
		return branchExists(branchName);
	}
	async getBranchCommit(branchName) {
		const searchConfig = {
			state: "open",
			branchName,
			singleChange: true,
			requestDetails: ["CURRENT_REVISION"]
		};
		const change = (await client.findChanges(repository, searchConfig)).pop();
		if (isLongCommitSha(change?.current_revision)) return change.current_revision;
		return getBranchCommit(branchName);
	}
	async getBranchUpdateDate(branchName) {
		const searchConfig = {
			state: "open",
			branchName,
			singleChange: true,
			refreshCache: true,
			requestDetails: ["CURRENT_REVISION"]
		};
		const change = (await client.findChanges(repository, searchConfig)).pop();
		if (change) {
			const date = convertGerritDateToISO(change.revisions[change.current_revision].created);
			return DateTime.fromISO(date).toUTC();
		}
		return getBranchUpdateDate(branchName);
	}
	async isBranchBehindBase(branchName, baseBranch) {
		const searchConfig = {
			state: "open",
			branchName,
			targetBranch: baseBranch,
			singleChange: true,
			requestDetails: ["CURRENT_REVISION", "CURRENT_ACTIONS"]
		};
		const change = (await client.findChanges(repository, searchConfig)).pop();
		if (change) return change.revisions[change.current_revision].actions.rebase.enabled === true;
		return true;
	}
	async isBranchConflicted(baseBranch, branch) {
		const searchConfig = {
			state: "open",
			branchName: branch,
			targetBranch: baseBranch,
			singleChange: true
		};
		const change = (await client.findChanges(repository, searchConfig)).pop();
		if (change) return !(await client.getMergeableInfo(change)).mergeable;
		else {
			logger.warn({
				branch,
				baseBranch
			}, "There is no open change with this branch");
			return true;
		}
	}
	async isBranchModified(branchName, baseBranch) {
		const searchConfig = {
			state: "open",
			branchName,
			targetBranch: baseBranch,
			singleChange: true,
			requestDetails: ["CURRENT_REVISION", "DETAILED_ACCOUNTS"]
		};
		const change = (await client.findChanges(repository, searchConfig)).pop();
		if (change) return change.revisions[change.current_revision].uploader.username !== username;
		return false;
	}
	async commitAndPush(commit) {
		logger.debug(`commitAndPush(${commit.branchName})`);
		const existingChange = await client.getBranchChange(repository, {
			branchName: commit.branchName,
			state: "open",
			targetBranch: commit.baseBranch,
			requestDetails: ["CURRENT_REVISION"]
		});
		let hasChanges = true;
		const message = typeof commit.message === "string" ? [commit.message] : commit.message;
		// v8 ignore else -- TODO: add test #40625
		if (commit.prTitle) {
			const firstMessageLines = message[0].split("\n");
			firstMessageLines[0] = commit.prTitle;
			message[0] = firstMessageLines.join("\n");
		}
		const changeId = existingChange?.change_id ?? generateChangeId();
		commit.message = [...message, `Renovate-Branch: ${commit.branchName}\nChange-Id: ${changeId}`];
		const commitResult = await prepareCommit({
			...commit,
			force: true
		});
		if (commitResult) {
			const { commitSha } = commitResult;
			if (existingChange) {
				const fetchRefSpec = existingChange.revisions[existingChange.current_revision].ref;
				await fetchRevSpec(fetchRefSpec);
				hasChanges = await hasDiff("HEAD", "FETCH_HEAD");
				if (hasChanges || commit.force) {
					/* v8 ignore else -- should never happen */
					if (await pushForReview({
						sourceRef: commit.branchName,
						targetBranch: existingChange.branch,
						files: commit.files,
						autoApprove: commit.autoApprove
					})) return commitSha;
				}
			} else {
				logger.debug(`Commit prepared, push deferred to createPr()`);
				pendingChangeBranches.add(commit.branchName);
				return commitSha;
			}
		}
		return null;
	}
	async deleteBranch(branchName) {
		pendingChangeBranches.delete(branchName);
		await deleteBranch(branchName, { localBranch: true });
	}
	async mergeToLocal(branchName) {
		if (pendingChangeBranches.has(branchName)) {
			logger.debug(`Merging local branch ${branchName} (not yet pushed)`);
			return mergeToLocal(branchName, { localBranch: true });
		}
		const searchConfig = {
			state: "open",
			branchName,
			singleChange: true,
			requestDetails: ["CURRENT_REVISION"]
		};
		const change = (await client.findChanges(repository, searchConfig)).pop();
		if (change) {
			const currentRevision = change.revisions[change.current_revision];
			return super.mergeToLocal(currentRevision.ref);
		}
		return super.mergeToLocal(branchName);
	}
};
/**
* This function should generate a Gerrit Change-ID analogous to the commit hook. We avoid the commit hook cause of security concerns.
* random=$( (whoami ; hostname ; date; cat $1 ; echo $RANDOM) | git hash-object --stdin) prefixed with an 'I'.
* TODO: Gerrit don't accept longer Change-IDs (sha256), but what happens with this https://git-scm.com/docs/hash-function-transition/ ?
*/
function generateChangeId() {
	return `I${hash(randomUUID(), "sha1")}`;
}
//#endregion
export { GerritScm, configureScm, pendingChangeBranches, pushForReview };

//# sourceMappingURL=scm.js.map