import { logger } from "../../../logger/index.js";
import { hash } from "../../../util/hash.js";
import { branchExists, fetchRevSpec, getBranchCommit, getBranchUpdateDate, hasDiff, prepareCommit, pushCommit } from "../../../util/git/index.js";
import { DefaultGitScm } from "../default-scm.js";
import { convertGerritDateToISO } from "./utils.js";
import { client } from "./client.js";
import { DateTime } from "luxon";
import { randomUUID } from "crypto";

//#region lib/modules/platform/gerrit/scm.ts
let repository;
let username;
function configureScm(repo, login) {
	repository = repo;
	username = login;
}
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 (change) 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) {
				const pushOptions = ["notify=NONE"];
				if (commit.autoApprove) pushOptions.push("label=Code-Review+2");
				if (commit.labels) for (const label of commit.labels) pushOptions.push(`hashtag=${label}`);
				const changeBranch = existingChange?.branch ?? commit.baseBranch;
				// v8 ignore else -- TODO: add test #40625
				if (await pushCommit({
					sourceRef: commit.branchName,
					targetRef: `refs/for/${changeBranch}`,
					files: commit.files,
					pushOptions
				})) return commitSha;
			}
		}
		return null;
	}
	deleteBranch(branchName) {
		return Promise.resolve();
	}
	async mergeToLocal(branchName) {
		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 };
//# sourceMappingURL=scm.js.map