"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.clear = exports.getAll = exports.findAll = exports.hostType = exports.hosts = exports.find = exports.add = exports.migrateRule = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const logger_1 = require("../logger");
const clone_1 = require("./clone");
const sanitize = tslib_1.__importStar(require("./sanitize"));
const string_1 = require("./string");
const url_1 = require("./url");
let hostRules = [];
function migrateRule(rule) {
    const cloned = (0, clone_1.clone)(rule);
    delete cloned.hostName;
    delete cloned.domainName;
    delete cloned.baseUrl;
    const result = cloned;
    const { matchHost } = result;
    const { hostName, domainName, baseUrl } = rule;
    const hostValues = [matchHost, hostName, domainName, baseUrl].filter(Boolean);
    if (hostValues.length === 1) {
        const [matchHost] = hostValues;
        result.matchHost = matchHost;
    }
    else if (hostValues.length > 1) {
        throw new Error(`hostRules cannot contain more than one host-matching field - use "matchHost" only.`);
    }
    return result;
}
exports.migrateRule = migrateRule;
function add(params) {
    const rule = migrateRule(params);
    const confidentialFields = ['password', 'token'];
    if (rule.matchHost) {
        rule.matchHost = (0, url_1.massageHostUrl)(rule.matchHost);
        const parsedUrl = (0, url_1.parseUrl)(rule.matchHost);
        rule.resolvedHost = parsedUrl?.hostname ?? rule.matchHost;
        confidentialFields.forEach((field) => {
            if (rule[field]) {
                logger_1.logger.debug(
                // TODO: types (#22198)
                `Adding ${field} authentication for ${rule.matchHost} (hostType=${rule.hostType}) to hostRules`);
            }
        });
    }
    confidentialFields.forEach((field) => {
        const secret = rule[field];
        if (is_1.default.string(secret) && secret.length > 3) {
            sanitize.addSecretForSanitizing(secret);
        }
    });
    if (rule.username && rule.password) {
        sanitize.addSecretForSanitizing((0, string_1.toBase64)(`${rule.username}:${rule.password}`));
    }
    hostRules.push(rule);
}
exports.add = add;
function matchesHost(url, matchHost) {
    if ((0, url_1.isHttpUrl)(url) && (0, url_1.isHttpUrl)(matchHost)) {
        return url.startsWith(matchHost);
    }
    const parsedUrl = (0, url_1.parseUrl)(url);
    if (!parsedUrl) {
        return false;
    }
    const { hostname } = parsedUrl;
    if (!hostname) {
        return false;
    }
    if (hostname === matchHost) {
        return true;
    }
    const topLevelSuffix = matchHost.startsWith('.')
        ? matchHost
        : `.${matchHost}`;
    return hostname.endsWith(topLevelSuffix);
}
function fromShorterToLongerMatchHost(a, b) {
    if (!a.matchHost || !b.matchHost) {
        return 0;
    }
    return a.matchHost.length - b.matchHost.length;
}
function hostRuleRank({ hostType, matchHost, readOnly }) {
    // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
    if ((hostType || readOnly) && matchHost) {
        return 3;
    }
    if (matchHost) {
        return 2;
    }
    if (hostType) {
        return 1;
    }
    return 0;
}
function fromLowerToHigherRank(a, b) {
    return hostRuleRank(a) - hostRuleRank(b);
}
function find(search) {
    if ([search.hostType, search.url].every(is_1.default.falsy)) {
        logger_1.logger.warn({ search }, 'Invalid hostRules search');
        return {};
    }
    // Sort primarily by rank, and secondarily by matchHost length
    const sortedRules = hostRules
        .sort(fromShorterToLongerMatchHost)
        .sort(fromLowerToHigherRank);
    const matchedRules = [];
    for (const rule of sortedRules) {
        let hostTypeMatch = true;
        let hostMatch = true;
        let readOnlyMatch = true;
        if (rule.hostType) {
            hostTypeMatch = false;
            if (search.hostType === rule.hostType) {
                hostTypeMatch = true;
            }
        }
        if (rule.matchHost && rule.resolvedHost) {
            hostMatch = false;
            if (search.url) {
                hostMatch = matchesHost(search.url, rule.matchHost);
            }
        }
        if (!is_1.default.undefined(rule.readOnly)) {
            readOnlyMatch = false;
            if (search.readOnly === rule.readOnly) {
                readOnlyMatch = true;
                hostTypeMatch = true; // When we match `readOnly`, we don't care about `hostType`
            }
        }
        if (hostTypeMatch && readOnlyMatch && hostMatch) {
            matchedRules.push((0, clone_1.clone)(rule));
        }
    }
    const res = Object.assign({}, ...matchedRules);
    delete res.hostType;
    delete res.resolvedHost;
    delete res.matchHost;
    delete res.readOnly;
    return res;
}
exports.find = find;
function hosts({ hostType }) {
    return hostRules
        .filter((rule) => rule.hostType === hostType)
        .map((rule) => rule.resolvedHost)
        .filter(is_1.default.truthy);
}
exports.hosts = hosts;
function hostType({ url }) {
    return (hostRules
        .filter((rule) => rule.matchHost && matchesHost(url, rule.matchHost))
        .sort(fromShorterToLongerMatchHost)
        .map((rule) => rule.hostType)
        .filter(is_1.default.truthy)
        .pop() ?? null);
}
exports.hostType = hostType;
function findAll({ hostType }) {
    return hostRules.filter((rule) => rule.hostType === hostType);
}
exports.findAll = findAll;
/**
 * @returns a deep copy of all known host rules without any filtering
 */
function getAll() {
    return (0, clone_1.clone)(hostRules);
}
exports.getAll = getAll;
function clear() {
    logger_1.logger.debug('Clearing hostRules');
    hostRules = [];
    sanitize.clearRepoSanitizedSecretsList();
}
exports.clear = clear;
//# sourceMappingURL=host-rules.js.map