"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIssueList = exports.findIssue = exports.ensureIssue = exports.ensureIssueClosing = exports.ensureCommentRemoval = exports.deleteLabel = exports.massageMarkdown = exports.ensureComment = exports.addAssignees = exports.addReviewers = exports.getJsonFile = exports.getRawFile = exports.setBranchStatus = exports.getBranchStatusCheck = exports.getBranchStatus = exports.mergePr = exports.getPrList = exports.getBranchPr = exports.createPr = exports.updatePr = exports.getPr = exports.findPr = exports.initRepo = exports.getRepos = exports.initPlatform = exports.writeToConfig = exports.id = void 0;
const tslib_1 = require("tslib");
const logger_1 = require("../../../logger");
const common_1 = require("../../../util/common");
const git = tslib_1.__importStar(require("../../../util/git"));
const gerrit_1 = require("../../../util/http/gerrit");
const regex_1 = require("../../../util/regex");
const url_1 = require("../../../util/url");
const util_1 = require("../util");
const pr_body_1 = require("../utils/pr-body");
const read_only_issue_body_1 = require("../utils/read-only-issue-body");
const client_1 = require("./client");
const scm_1 = require("./scm");
const utils_1 = require("./utils");
exports.id = 'gerrit';
const defaults = {};
let config = {
    labels: {},
};
function writeToConfig(newConfig) {
    config = { ...config, ...newConfig };
}
exports.writeToConfig = writeToConfig;
function initPlatform({ endpoint, username, password, }) {
    logger_1.logger.debug(`initPlatform(${endpoint}, ${username})`);
    if (!endpoint) {
        throw new Error('Init: You must configure a Gerrit Server endpoint');
    }
    if (!(username && password)) {
        throw new Error('Init: You must configure a Gerrit Server username/password');
    }
    config.gerritUsername = username;
    defaults.endpoint = (0, url_1.ensureTrailingSlash)(endpoint);
    (0, gerrit_1.setBaseUrl)(defaults.endpoint);
    const platformConfig = {
        endpoint: defaults.endpoint,
    };
    return Promise.resolve(platformConfig);
}
exports.initPlatform = initPlatform;
/**
 * Get all state="ACTIVE" and type="CODE" repositories from gerrit
 */
async function getRepos() {
    logger_1.logger.debug(`getRepos()`);
    return await client_1.client.getRepos();
}
exports.getRepos = getRepos;
/**
 * Clone repository to local directory
 * @param config
 */
async function initRepo({ repository, gitUrl, }) {
    logger_1.logger.debug(`initRepo(${repository}, ${gitUrl})`);
    const projectInfo = await client_1.client.getProjectInfo(repository);
    const branchInfo = await client_1.client.getBranchInfo(repository);
    config = {
        ...config,
        repository,
        head: branchInfo.revision,
        config: projectInfo,
        labels: projectInfo.labels ?? {},
    };
    const baseUrl = defaults.endpoint;
    const url = (0, utils_1.getGerritRepoUrl)(repository, baseUrl);
    (0, scm_1.configureScm)(repository, config.gerritUsername);
    await git.initRepo({ url });
    //abandon "open" and "rejected" changes at startup
    const rejectedChanges = await client_1.client.findChanges(config.repository, {
        branchName: '',
        state: 'open',
        label: '-2',
    });
    for (const change of rejectedChanges) {
        await client_1.client.abandonChange(change._number);
    }
    const repoConfig = {
        defaultBranch: config.head,
        isFork: false,
        repoFingerprint: (0, util_1.repoFingerprint)(repository, baseUrl),
    };
    return repoConfig;
}
exports.initRepo = initRepo;
async function findPr(findPRConfig, refreshCache) {
    const change = (await client_1.client.findChanges(config.repository, findPRConfig, refreshCache)).pop();
    return change ? (0, utils_1.mapGerritChangeToPr)(change) : null;
}
exports.findPr = findPr;
async function getPr(number) {
    try {
        const change = await client_1.client.getChange(number);
        return (0, utils_1.mapGerritChangeToPr)(change);
    }
    catch (err) {
        if (err.statusCode === 404) {
            return null;
        }
        throw err;
    }
}
exports.getPr = getPr;
async function updatePr(prConfig) {
    logger_1.logger.debug(`updatePr(${prConfig.number}, ${prConfig.prTitle})`);
    const change = await client_1.client.getChange(prConfig.number);
    if (change.subject !== prConfig.prTitle) {
        await client_1.client.updateCommitMessage(prConfig.number, change.change_id, prConfig.prTitle);
    }
    if (prConfig.prBody) {
        await client_1.client.addMessageIfNotAlreadyExists(prConfig.number, prConfig.prBody, utils_1.TAG_PULL_REQUEST_BODY);
    }
    if (prConfig.platformOptions?.autoApprove) {
        await client_1.client.approveChange(prConfig.number);
    }
    if (prConfig.state && prConfig.state === 'closed') {
        await client_1.client.abandonChange(prConfig.number);
    }
}
exports.updatePr = updatePr;
async function createPr(prConfig) {
    logger_1.logger.debug(`createPr(${prConfig.sourceBranch}, ${prConfig.prTitle}, ${prConfig.labels?.toString() ?? ''})`);
    const pr = (await client_1.client.findChanges(config.repository, {
        branchName: prConfig.sourceBranch,
        targetBranch: prConfig.targetBranch,
        state: 'open',
    }, true)).pop();
    if (pr === undefined) {
        throw new Error(`the change should be created automatically from previous push to refs/for/${prConfig.sourceBranch}`);
    }
    //Workaround for "Known Problems.1"
    if (pr.subject !== prConfig.prTitle) {
        await client_1.client.updateCommitMessage(pr._number, pr.change_id, prConfig.prTitle);
    }
    await client_1.client.addMessageIfNotAlreadyExists(pr._number, prConfig.prBody, utils_1.TAG_PULL_REQUEST_BODY);
    if (prConfig.platformOptions?.autoApprove) {
        await client_1.client.approveChange(pr._number);
    }
    return getPr(pr._number);
}
exports.createPr = createPr;
async function getBranchPr(branchName) {
    const change = (await client_1.client.findChanges(config.repository, { branchName, state: 'open' })).pop();
    return change ? (0, utils_1.mapGerritChangeToPr)(change) : null;
}
exports.getBranchPr = getBranchPr;
function getPrList() {
    return client_1.client
        .findChanges(config.repository, { branchName: '' })
        .then((res) => res.map((change) => (0, utils_1.mapGerritChangeToPr)(change)));
}
exports.getPrList = getPrList;
async function mergePr(config) {
    logger_1.logger.debug(`mergePr(${config.id}, ${config.branchName}, ${config.strategy})`);
    try {
        const change = await client_1.client.submitChange(config.id);
        return change.status === 'MERGED';
    }
    catch (err) {
        if (err.statusCode === 409) {
            logger_1.logger.warn({ err }, "Can't submit the change, because the submit rule doesn't allow it.");
            return false;
        }
        throw err;
    }
}
exports.mergePr = mergePr;
/**
 * BranchStatus for Gerrit assumes that the branchName refers to a change.
 * @param branchName
 */
async function getBranchStatus(branchName) {
    logger_1.logger.debug(`getBranchStatus(${branchName})`);
    const changes = await client_1.client.findChanges(config.repository, { state: 'open', branchName }, true);
    if (changes.length > 0) {
        const allSubmittable = changes.filter((change) => change.submittable === true).length ===
            changes.length;
        if (allSubmittable) {
            return 'green';
        }
        const hasProblems = changes.filter((change) => change.problems.length > 0).length > 0;
        if (hasProblems) {
            return 'red';
        }
    }
    return 'yellow';
}
exports.getBranchStatus = getBranchStatus;
/**
 * check the gerrit-change for the presence of the corresponding "$context" Gerrit label if configured,
 *  return 'yellow' if not configured or not set
 * @param branchName
 * @param context renovate/stability-days || ...
 */
async function getBranchStatusCheck(branchName, context) {
    const label = config.labels[context];
    if (label) {
        const change = (await client_1.client.findChanges(config.repository, { branchName, state: 'open' }, true)).pop();
        if (change) {
            const labelRes = change.labels?.[context];
            if (labelRes) {
                if (labelRes.approved) {
                    return 'green';
                }
                if (labelRes.rejected) {
                    return 'red';
                }
            }
        }
    }
    return 'yellow';
}
exports.getBranchStatusCheck = getBranchStatusCheck;
/**
 * Apply the branch state $context to the corresponding gerrit label (if available)
 * context === "renovate/stability-days" / "renovate/merge-confidence" and state === "green"/...
 * @param branchStatusConfig
 */
async function setBranchStatus(branchStatusConfig) {
    const label = config.labels[branchStatusConfig.context];
    const labelValue = label && (0, utils_1.mapBranchStatusToLabel)(branchStatusConfig.state, label);
    if (branchStatusConfig.context && labelValue) {
        const pr = await getBranchPr(branchStatusConfig.branchName);
        if (pr === null) {
            return;
        }
        await client_1.client.setLabel(pr.number, branchStatusConfig.context, labelValue);
    }
}
exports.setBranchStatus = setBranchStatus;
function getRawFile(fileName, repoName, branchOrTag) {
    const repo = repoName ?? config.repository ?? 'All-Projects';
    const branch = branchOrTag ?? (repo === config.repository ? config.head : 'HEAD');
    return client_1.client.getFile(repo, branch, fileName);
}
exports.getRawFile = getRawFile;
async function getJsonFile(fileName, repoName, branchOrTag) {
    const raw = await getRawFile(fileName, repoName, branchOrTag);
    return (0, common_1.parseJson)(raw, fileName);
}
exports.getJsonFile = getJsonFile;
async function addReviewers(number, reviewers) {
    for (const reviewer of reviewers) {
        await client_1.client.addReviewer(number, reviewer);
    }
}
exports.addReviewers = addReviewers;
/**
 * add "CC" (only one possible)
 */
async function addAssignees(number, assignees) {
    if (assignees.length) {
        if (assignees.length > 1) {
            logger_1.logger.debug(`addAssignees(${number}, ${assignees.toString()}) called with more then one assignee! Gerrit only supports one assignee! Using the first from list.`);
        }
        await client_1.client.addAssignee(number, assignees[0]);
    }
}
exports.addAssignees = addAssignees;
async function ensureComment(ensureComment) {
    logger_1.logger.debug(`ensureComment(${ensureComment.number}, ${ensureComment.topic}, ${ensureComment.content})`);
    await client_1.client.addMessageIfNotAlreadyExists(ensureComment.number, ensureComment.content, ensureComment.topic ?? undefined);
    return true;
}
exports.ensureComment = ensureComment;
function massageMarkdown(prBody) {
    //TODO: do more Gerrit specific replacements?
    return (0, pr_body_1.smartTruncate)((0, read_only_issue_body_1.readOnlyIssueBody)(prBody), 16384) //TODO: check the real gerrit limit (max. chars)
        .replace((0, regex_1.regEx)(/Pull Request(s)?/g), 'Change-Request$1')
        .replace((0, regex_1.regEx)(/\bPR(s)?\b/g), 'Change-Request$1')
        .replace((0, regex_1.regEx)(/<\/?summary>/g), '**')
        .replace((0, regex_1.regEx)(/<\/?details>/g), '')
        .replace((0, regex_1.regEx)(/&#8203;/g), '') //remove zero-width-space not supported in gerrit-markdown
        .replace('close this Change-Request unmerged.', 'abandon or down vote this Change-Request with -2.')
        .replace('Branch creation', 'Change creation')
        .replace('Close this Change-Request', 'Down-vote this Change-Request with -2')
        .replace('you tick the rebase/retry checkbox', 'add "rebase!" at the beginning of the commit message.')
        .replace((0, regex_1.regEx)(`\n---\n\n.*?<!-- rebase-check -->.*?\n`), '')
        .replace((0, regex_1.regEx)(/<!--renovate-(?:debug|config-hash):.*?-->/g), '');
}
exports.massageMarkdown = massageMarkdown;
function deleteLabel(number, label) {
    return Promise.resolve();
}
exports.deleteLabel = deleteLabel;
function ensureCommentRemoval(ensureCommentRemoval) {
    return Promise.resolve();
}
exports.ensureCommentRemoval = ensureCommentRemoval;
function ensureIssueClosing(title) {
    return Promise.resolve();
}
exports.ensureIssueClosing = ensureIssueClosing;
function ensureIssue(issueConfig) {
    return Promise.resolve(null);
}
exports.ensureIssue = ensureIssue;
function findIssue(title) {
    return Promise.resolve(null);
}
exports.findIssue = findIssue;
function getIssueList() {
    return Promise.resolve([]);
}
exports.getIssueList = getIssueList;
//# sourceMappingURL=index.js.map