"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.codeOwnersForPr = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const ignore_1 = tslib_1.__importDefault(require("ignore"));
const logger_1 = require("../../../../logger");
const fs_1 = require("../../../../util/fs");
const git_1 = require("../../../../util/git");
const regex_1 = require("../../../../util/regex");
function extractOwnersFromLine(line) {
    const [pattern, ...usernames] = line.split((0, regex_1.regEx)(/\s+/));
    const matchPattern = (0, ignore_1.default)().add(pattern);
    return {
        usernames,
        pattern,
        score: pattern.length,
        match: (path) => matchPattern.ignores(path),
    };
}
function matchFileToOwners(file, rules) {
    const usernames = new Map();
    for (const rule of rules) {
        if (!rule.match(file)) {
            continue;
        }
        for (const user of rule.usernames) {
            usernames.set(user, rule.score);
        }
    }
    return { file, userScoreMap: usernames };
}
function getOwnerList(filesWithOwners) {
    const userFileMap = new Map();
    for (const fileMatch of filesWithOwners) {
        for (const [username, score] of fileMatch.userScoreMap.entries()) {
            // Get / create user file score
            const fileMap = userFileMap.get(username) ?? new Map();
            if (!userFileMap.has(username)) {
                userFileMap.set(username, fileMap);
            }
            // Add file to user
            fileMap.set(fileMatch.file, (fileMap.get(fileMatch.file) ?? 0) + score);
        }
    }
    return Array.from(userFileMap.entries()).map(([key, value]) => ({
        username: key,
        fileScoreMap: value,
    }));
}
async function codeOwnersForPr(pr) {
    logger_1.logger.debug('Searching for CODEOWNERS file');
    try {
        // Find CODEOWNERS file
        const codeOwnersFile = (await (0, fs_1.readLocalFile)('CODEOWNERS', 'utf8')) ??
            (await (0, fs_1.readLocalFile)('.github/CODEOWNERS', 'utf8')) ??
            (await (0, fs_1.readLocalFile)('.gitlab/CODEOWNERS', 'utf8')) ??
            (await (0, fs_1.readLocalFile)('docs/CODEOWNERS', 'utf8'));
        if (!codeOwnersFile) {
            logger_1.logger.debug('No CODEOWNERS file found');
            return [];
        }
        logger_1.logger.debug(`Found CODEOWNERS file: ${codeOwnersFile}`);
        // Get list of modified files in PR
        const prFiles = await (0, git_1.getBranchFiles)(pr.sourceBranch);
        if (!prFiles?.length) {
            logger_1.logger.debug('PR includes no files');
            return [];
        }
        // Convert CODEOWNERS file into list of matching rules
        const fileOwnerRules = codeOwnersFile
            .split(regex_1.newlineRegex)
            // Remove comments
            .map((line) => line.split('#')[0])
            // Remove empty lines
            .map((line) => line.trim())
            .filter(is_1.default.nonEmptyString)
            // Extract pattern & usernames
            .map(extractOwnersFromLine);
        logger_1.logger.debug({ prFiles, fileOwnerRules }, 'PR files and rules to match for CODEOWNERS');
        // Apply rules & get list of owners for each prFile
        const emptyRules = fileOwnerRules.filter((rule) => rule.usernames.length === 0);
        const fileOwners = 
        // Map through all prFiles and match said file(s) with all the rules
        prFiles
            .map((file) => matchFileToOwners(file, fileOwnerRules))
            // Match file again but this time only with emptyRules, to ensure that files which have no owner set remain owner-less
            .map((fileMatch) => {
            const matchEmpty = emptyRules.find((rule) => rule.match(fileMatch.file));
            if (matchEmpty) {
                return { ...fileMatch, userScoreMap: new Map() };
            }
            return fileMatch;
        });
        logger_1.logger.debug(`CODEOWNERS matched the following files: ${fileOwners
            .map((f) => f.file)
            .join(', ')}`);
        // Get list of all matched users and the files they own (reverse keys of fileOwners)
        const usersWithOwnedFiles = getOwnerList(fileOwners);
        // Calculate a match score for each user. This allows sorting of the final user array in a way that guarantees that users matched with more precise patterns are first and users matched with less precise patterns are last (wildcards)
        const userScore = usersWithOwnedFiles
            .map((userMatch) => ({
            user: userMatch.username,
            score: Array.from(userMatch.fileScoreMap.values()).reduce((acc, score) => acc + score, 0),
        }))
            .sort((a, b) => b.score - a.score);
        logger_1.logger.debug(`CODEOWNERS matched the following users: ${JSON.stringify(userScore)}`);
        return userScore.map((u) => u.user);
    }
    catch (err) {
        logger_1.logger.warn({ err, pr }, 'Failed to determine CODEOWNERS for PR.');
        return [];
    }
}
exports.codeOwnersForPr = codeOwnersForPr;
//# sourceMappingURL=code-owners.js.map