"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChangeLogSource = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const logger_1 = require("../../../../../logger");
const datasource_1 = require("../../../../../modules/datasource");
const allVersioning = tslib_1.__importStar(require("../../../../../modules/versioning"));
const packageCache = tslib_1.__importStar(require("../../../../../util/cache/package"));
const memoize_1 = require("../../../../../util/memoize");
const regex_1 = require("../../../../../util/regex");
const url_1 = require("../../../../../util/url");
const common_1 = require("./common");
const release_notes_1 = require("./release-notes");
const releases_1 = require("./releases");
class ChangeLogSource {
    platform;
    datasource;
    cacheNamespace;
    constructor(platform, datasource) {
        this.platform = platform;
        this.datasource = datasource;
        this.cacheNamespace = `changelog-${platform}-release`;
    }
    async getAllTags(endpoint, repository) {
        const tags = (await (0, datasource_1.getPkgReleases)({
            registryUrls: [endpoint],
            datasource: this.datasource,
            packageName: repository,
            versioning: 'regex:(?<major>\\d+)(\\.(?<minor>\\d+))?(\\.(?<patch>\\d+))?',
        }))?.releases;
        if (is_1.default.nullOrUndefined(tags) || is_1.default.emptyArray(tags)) {
            logger_1.logger.debug(`No ${this.datasource} tags found for repository: ${repository}`);
            return [];
        }
        return tags.map(({ version }) => version);
    }
    async getChangeLogJSON(config) {
        logger_1.logger.trace(`getChangeLogJSON for ${this.platform}`);
        const versioning = config.versioning;
        const currentVersion = config.currentVersion;
        const newVersion = config.newVersion;
        const sourceUrl = config.sourceUrl;
        const packageName = config.packageName;
        const depName = config.depName;
        const sourceDirectory = config.sourceDirectory;
        const versioningApi = allVersioning.get(versioning);
        if (this.shouldSkipPackage(config)) {
            return null;
        }
        const baseUrl = this.getBaseUrl(config);
        const apiBaseUrl = this.getAPIBaseUrl(config);
        const repository = this.getRepositoryFromUrl(config);
        const tokenResponse = this.hasValidToken(config);
        if (!tokenResponse.isValid) {
            if (tokenResponse.error) {
                return {
                    error: tokenResponse.error,
                };
            }
            return null;
        }
        if (is_1.default.falsy(this.hasValidRepository(repository))) {
            logger_1.logger.debug(`Invalid ${this.platform} URL found: ${sourceUrl}`);
            return null;
        }
        const releases = config.releases ?? (await (0, releases_1.getInRangeReleases)(config));
        if (!releases?.length) {
            logger_1.logger.debug('No releases');
            return null;
        }
        // This extra filter/sort should not be necessary, but better safe than sorry
        const validReleases = [...releases]
            .filter((release) => versioningApi.isVersion(release.version))
            .sort((a, b) => versioningApi.sortVersions(a.version, b.version));
        if (validReleases.length < 2) {
            logger_1.logger.debug(`Not enough valid releases for dep ${depName} (${packageName})`);
            return null;
        }
        const changelogReleases = [];
        // Check if `v` belongs to the range (currentVersion, newVersion]
        const inRange = (v) => versioningApi.isGreaterThan(v, currentVersion) &&
            !versioningApi.isGreaterThan(v, newVersion);
        const getTags = (0, memoize_1.memoize)(() => this.getAllTags(apiBaseUrl, repository));
        for (let i = 1; i < validReleases.length; i += 1) {
            const prev = validReleases[i - 1];
            const next = validReleases[i];
            if (!inRange(next.version)) {
                continue;
            }
            let release = await packageCache.get(this.cacheNamespace, this.getCacheKey(sourceUrl, packageName, prev.version, next.version));
            if (!release) {
                release = {
                    version: next.version,
                    date: next.releaseTimestamp,
                    gitRef: next.gitRef,
                    // put empty changes so that existing templates won't break
                    changes: [],
                    compare: {},
                };
                const tags = await getTags();
                const prevHead = this.getRef(versioningApi, packageName, depName, prev, tags);
                const nextHead = this.getRef(versioningApi, packageName, depName, next, tags);
                if (is_1.default.nonEmptyString(prevHead) && is_1.default.nonEmptyString(nextHead)) {
                    release.compare.url = this.getCompareURL(baseUrl, repository, prevHead, nextHead);
                }
                const cacheMinutes = 55;
                await packageCache.set(this.cacheNamespace, this.getCacheKey(sourceUrl, packageName, prev.version, next.version), release, cacheMinutes);
            }
            changelogReleases.unshift(release);
        }
        let res = {
            project: {
                apiBaseUrl,
                baseUrl,
                type: this.platform,
                repository,
                sourceUrl,
                sourceDirectory,
                packageName,
            },
            versions: changelogReleases,
        };
        res = await (0, release_notes_1.addReleaseNotes)(res, config);
        return res;
    }
    findTagOfRelease(versioningApi, packageName, depName, depNewVersion, tags) {
        const releaseRegexPrefix = `^(?:${packageName}|${depName}|release)[@_-]v?`;
        const regex = (0, regex_1.regEx)(releaseRegexPrefix, undefined, false);
        const exactReleaseRegex = (0, regex_1.regEx)(`${releaseRegexPrefix}${depNewVersion}`);
        const exactTagsList = tags.filter((tag) => {
            return exactReleaseRegex.test(tag);
        });
        const tagList = exactTagsList.length ? exactTagsList : tags;
        return tagList
            .filter((tag) => versioningApi.isVersion(tag.replace(regex, '')))
            .find((tag) => versioningApi.equals(tag.replace(regex, ''), depNewVersion));
    }
    getRef(versioningApi, packageName, depName, release, tags) {
        const tagName = this.findTagOfRelease(versioningApi, packageName, depName, release.version, tags);
        if (is_1.default.nonEmptyString(tagName)) {
            return tagName;
        }
        if (is_1.default.nonEmptyString(release.gitRef)) {
            return release.gitRef;
        }
        return null;
    }
    getCacheKey(sourceUrl, packageName, prev, next) {
        return `${(0, common_1.slugifyUrl)(sourceUrl)}:${packageName}:${prev}:${next}`;
    }
    getBaseUrl(config) {
        const parsedUrl = (0, url_1.parseUrl)(config.sourceUrl);
        if (is_1.default.nullOrUndefined(parsedUrl)) {
            return '';
        }
        const protocol = parsedUrl.protocol.replace((0, regex_1.regEx)(/^git\+/), '');
        const host = parsedUrl.host;
        return `${protocol}//${host}/`;
    }
    getRepositoryFromUrl(config) {
        const parsedUrl = (0, url_1.parseUrl)(config.sourceUrl);
        if (is_1.default.nullOrUndefined(parsedUrl)) {
            return '';
        }
        const pathname = parsedUrl.pathname;
        return (0, url_1.trimSlashes)(pathname).replace((0, regex_1.regEx)(/\.git$/), '');
    }
    hasValidToken(config) {
        return { isValid: true };
    }
    shouldSkipPackage(config) {
        return false;
    }
    hasValidRepository(repository) {
        return repository.split('/').length === 2;
    }
}
exports.ChangeLogSource = ChangeLogSource;
//# sourceMappingURL=source.js.map