"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 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)({
            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 sourceDirectory = config.sourceDirectory;
        const version = 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 (repository.split('/').length !== 2) {
            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) => version.isVersion(release.version))
            .sort((a, b) => version.sortVersions(a.version, b.version));
        if (validReleases.length < 2) {
            logger_1.logger.debug(`Not enough valid releases for dep ${packageName}`);
            return null;
        }
        const changelogReleases = [];
        // compare versions
        const include = (v) => version.isGreaterThan(v, currentVersion) &&
            !version.isGreaterThan(v, newVersion);
        for (let i = 1; i < validReleases.length; i += 1) {
            const prev = validReleases[i - 1];
            const next = validReleases[i];
            if (!include(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 prevHead = await this.getRef(version, packageName, prev, apiBaseUrl, repository);
                const nextHead = await this.getRef(version, packageName, next, apiBaseUrl, repository);
                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(version, packageName, depNewVersion, tags) {
        const regex = (0, regex_1.regEx)(`(?:${packageName}|release)[@-]`, undefined, false);
        const exactReleaseRegex = (0, regex_1.regEx)(`${packageName}[@\\-_]v?${depNewVersion}`);
        const exactTagsList = tags.filter((tag) => {
            return exactReleaseRegex.test(tag);
        });
        const tagList = exactTagsList.length ? exactTagsList : tags;
        return tagList
            .filter((tag) => version.isVersion(tag.replace(regex, '')))
            .find((tag) => version.equals(tag.replace(regex, ''), depNewVersion));
    }
    async getRef(version, packageName, release, apiBaseUrl, repository) {
        const tags = await this.getAllTags(apiBaseUrl, repository);
        const tagName = this.findTagOfRelease(version, packageName, 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;
        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;
    }
}
exports.ChangeLogSource = ChangeLogSource;
//# sourceMappingURL=source.js.map