"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.api = exports.supportsRanges = exports.urls = exports.displayName = exports.id = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const regex_1 = require("../../../util/regex");
const generic_1 = require("../generic");
exports.id = 'rpm';
exports.displayName = 'RPM version';
exports.urls = [
    'https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/',
    'https://fedoraproject.org/wiki/Package_Versioning_Examples',
    'https://fedoraproject.org/wiki/User:Tibbs/TildeCaretVersioning',
];
exports.supportsRanges = false;
const alphaNumPattern = (0, regex_1.regEx)(/([a-zA-Z]+)|(\d+)|(~)/g);
const epochPattern = (0, regex_1.regEx)(/^\d+$/);
class RpmVersioningApi extends generic_1.GenericVersioningApi {
    /**
     * https://github.com/rpm-software-management/rpm/blob/e3c11a790367016aed7ea48cfcc78751a71ce862/rpmio/rpmvercmp.c#L16
     */
    _parse(version) {
        let remainingVersion = version;
        let epoch = 0;
        const epochIndex = remainingVersion.indexOf(':');
        if (epochIndex !== -1) {
            const epochStr = remainingVersion.slice(0, epochIndex);
            if (epochPattern.test(epochStr)) {
                epoch = parseInt(epochStr, 10);
            }
            else {
                return null;
            }
            remainingVersion = remainingVersion.slice(epochIndex + 1);
        }
        let upstreamVersion;
        let rpmRelease = '';
        let rpmPreRelease = '';
        let snapshot = '';
        const releaseIndex = remainingVersion.indexOf('-');
        const prereleaseIndex = remainingVersion.indexOf('~');
        // Note: There can be a snapshot if there is no prerelease. Snapshot always beat no snapshot,
        // so if there is 3.12.0-1 vs 3.12.0-1^20231110, the snapshot wins.
        // The logic below only creates snapshot IF there is a prerleease version. This logic is NOT
        // correct, but the result is still correct due to the caret being ignored in release, and
        // release continue comparing
        //
        // Note: If there IS a tilde preceding the caret, then snapshot DOES NOT win
        // Example: 3.12.0-1~^20231001 LOSES to 3.12.0-1 and
        // 3.12.0-1~^20231001 LOSES to 3.12.0-1^20231001
        const snapshotIndex = remainingVersion.indexOf('^');
        if (releaseIndex >= 0) {
            upstreamVersion = remainingVersion.slice(0, releaseIndex);
            // Do NOT splice out prerelease, we need to distinguish if the flag is set or not, regardless if there is a version.
            // The tilde will get filtered out during regex
            if (prereleaseIndex >= 0) {
                rpmRelease = remainingVersion.slice(releaseIndex, prereleaseIndex);
                if (snapshotIndex >= 0) {
                    rpmPreRelease = remainingVersion.slice(prereleaseIndex, snapshotIndex);
                    snapshot = remainingVersion.slice(snapshotIndex + 1);
                }
                else {
                    rpmPreRelease = remainingVersion.slice(prereleaseIndex);
                }
            }
            else {
                rpmRelease = remainingVersion.slice(releaseIndex + 1);
            }
        }
        else {
            upstreamVersion = remainingVersion;
        }
        const release = [...remainingVersion.matchAll((0, regex_1.regEx)(/\d+/g))].map((m) => parseInt(m[0], 10));
        return {
            epoch,
            upstreamVersion,
            rpmRelease,
            release,
            rpmPreRelease,
            snapshot,
        };
    }
    _compare_string(s1, s2) {
        if (s1 === s2) {
            return 0;
        }
        const minLength = Math.min(s1.length, s2.length);
        for (let i = 0; i < minLength; i++) {
            const c1 = s1[i];
            const c2 = s2[i];
            if (c1 === c2) {
                continue;
            }
            if (c1 > c2) {
                return 1;
            }
            else if (c1 < c2) {
                return -1;
            }
        }
        // Okay, they've been the exact same up until now, so return the longer one
        return s1.length > s2.length ? 1 : -1;
    }
    /**
     * Taken from https://github.com/rpm-software-management/rpm/blob/master/rpmio/rpmvercmp.c
     */
    _compare_glob(v1, v2) {
        if (v1 === v2) {
            return 0;
        }
        const matchesv1 = v1.match(alphaNumPattern) ?? [];
        const matchesv2 = v2.match(alphaNumPattern) ?? [];
        const matches = Math.min(matchesv1.length, matchesv2.length);
        for (let i = 0; i < matches; i++) {
            const matchv1 = matchesv1[i];
            const matchv2 = matchesv2[i];
            // compare tildes
            if (matchv1?.[0] === '~' || matchv2?.[0] === '~') {
                if (matchv1?.[0] !== '~') {
                    return 1;
                }
                if (matchv2?.[0] !== '~') {
                    return -1;
                }
            }
            if (is_1.default.numericString(matchv1?.[0])) {
                // numbers are greater than letters
                if (!is_1.default.numericString(matchv2?.[0])) {
                    return 1;
                }
                //We clearly have a number here, so return which is greater
                const result = matchv1.localeCompare(matchv2, undefined, {
                    numeric: true,
                });
                if (result === 0) {
                    continue;
                }
                return Math.sign(result);
            }
            else if (is_1.default.numericString(matchv2?.[0])) {
                return -1;
            }
            // We have two string globs, compare them
            const compared_value = this._compare_string(matchv1, matchv2);
            if (compared_value !== 0) {
                return compared_value;
            }
        }
        // segments were all the same, but separators were different
        if (matchesv1.length === matchesv2.length) {
            return 0;
        }
        // If there is a tilde in a segment past the minimum number of segments, find it
        if (matchesv1.length > matches && matchesv1[matches][0] === '~') {
            return -1;
        }
        if (matchesv2.length > matches && matchesv2[matches][0] === '~') {
            return 1;
        }
        // whichever has the most segments wins
        return matchesv1.length > matchesv2.length ? 1 : -1;
    }
    _compare(version, other) {
        const parsed1 = this._parse(version);
        const parsed2 = this._parse(other);
        if (!(parsed1 && parsed2)) {
            return 1;
        }
        // Greater epoch wins
        if (parsed1.epoch !== parsed2.epoch) {
            return Math.sign(parsed1.epoch - parsed2.epoch);
        }
        // Greater upstream version wins
        const upstreamVersionDifference = this._compare_glob(parsed1.upstreamVersion, parsed2.upstreamVersion);
        if (upstreamVersionDifference !== 0) {
            return upstreamVersionDifference;
        }
        // Greater release version wins
        const releaseVersionDifference = this._compare_glob(parsed1.rpmRelease, parsed2.rpmRelease);
        if (releaseVersionDifference !== 0) {
            return releaseVersionDifference;
        }
        // No Prerelease wins
        if (parsed1.rpmPreRelease === '' && parsed2.rpmPreRelease !== '') {
            return 1;
        }
        else if (parsed1.rpmPreRelease !== '' && parsed2.rpmPreRelease === '') {
            return -1;
        }
        const preReleaseDifference = this._compare_glob(parsed1.rpmPreRelease, parsed2.rpmPreRelease);
        if (preReleaseDifference !== 0) {
            return releaseVersionDifference;
        }
        // Greater Snapshot wins
        return this._compare_glob(parsed1.snapshot, parsed2.snapshot);
    }
}
exports.api = new RpmVersioningApi();
exports.default = exports.api;
//# sourceMappingURL=index.js.map