"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SbtPackageDatasource = void 0;
const tslib_1 = require("tslib");
const upath = tslib_1.__importStar(require("upath"));
const xmldoc_1 = require("xmldoc");
const logger_1 = require("../../../logger");
const packageCache = tslib_1.__importStar(require("../../../util/cache/package"));
const decorator_1 = require("../../../util/cache/package/decorator");
const http_1 = require("../../../util/http");
const regex_1 = require("../../../util/regex");
const url_1 = require("../../../util/url");
const ivyVersioning = tslib_1.__importStar(require("../../versioning/ivy"));
const compare_1 = require("../../versioning/maven/compare");
const maven_1 = require("../maven");
const common_1 = require("../maven/common");
const util_1 = require("../maven/util");
const metadata_1 = require("../metadata");
const util_2 = require("./util");
class SbtPackageDatasource extends maven_1.MavenDatasource {
    static id = 'sbt-package';
    defaultRegistryUrls = [common_1.MAVEN_REPO];
    defaultVersioning = ivyVersioning.id;
    registryStrategy = 'hunt';
    sourceUrlSupport = 'package';
    sourceUrlNote = 'The source URL is determined from the `scm` tags in the results.';
    constructor(id = SbtPackageDatasource.id) {
        super(id);
        this.http = new http_1.Http('sbt');
    }
    static parseDepCoordinate(packageName) {
        const [groupId, javaArtifactId] = packageName.split(':');
        const [artifactId, scalaVersion] = javaArtifactId.split('_');
        return { groupId, artifactId, scalaVersion };
    }
    async getSbtReleases(registryUrl, packageName) {
        const { groupId, artifactId, scalaVersion } = SbtPackageDatasource.parseDepCoordinate(packageName);
        const groupIdSplit = groupId.split('.');
        const repoRootUrl = (0, url_1.ensureTrailingSlash)(registryUrl);
        const validRootUrlKey = `valid-root-url:${registryUrl}:${packageName}`;
        const validRootUrl = await packageCache.get('datasource-sbt-package', validRootUrlKey);
        const packageRootUrls = [];
        // istanbul ignore if: not easily testable
        if (validRootUrl) {
            packageRootUrls.push(validRootUrl);
        }
        else {
            const packageRootUrlWith = (sep) => `${repoRootUrl}${groupIdSplit.join(sep)}`;
            packageRootUrls.push((0, url_1.ensureTrailingSlash)(packageRootUrlWith('/')));
            packageRootUrls.push((0, url_1.ensureTrailingSlash)(packageRootUrlWith('.')));
        }
        let dependencyUrl;
        let packageUrls;
        for (const packageRootUrl of packageRootUrls) {
            const res = await (0, util_1.downloadHttpProtocol)(this.http, packageRootUrl);
            if (!res) {
                continue;
            }
            await packageCache.set('datasource-sbt-package', validRootUrlKey, packageRootUrl, 30 * 24 * 60);
            dependencyUrl = (0, url_1.trimTrailingSlash)(packageRootUrl);
            const rootPath = new URL(packageRootUrl).pathname;
            const artifactSubdirs = (0, util_2.extractPageLinks)(res.body, (href) => {
                const path = href.replace(rootPath, '');
                if (path.startsWith(`${artifactId}_native`) ||
                    path.startsWith(`${artifactId}_sjs`)) {
                    return null;
                }
                if (path === artifactId || path.startsWith(`${artifactId}_`)) {
                    return (0, url_1.ensureTrailingSlash)(`${packageRootUrl}${path}`);
                }
                return null;
            });
            if (scalaVersion) {
                const scalaSubdir = artifactSubdirs.find((x) => x.endsWith(`/${artifactId}_${scalaVersion}/`));
                if (scalaSubdir) {
                    packageUrls = [scalaSubdir];
                    break;
                }
            }
            packageUrls = artifactSubdirs;
            break;
        }
        if (!packageUrls) {
            return null;
        }
        const invalidPackageUrlsKey = `invalid-package-urls:${registryUrl}:${packageName}`;
        const invalidPackageUrls = new Set(await packageCache.get('datasource-sbt-package', invalidPackageUrlsKey));
        packageUrls = packageUrls.filter((url) => !invalidPackageUrls.has(url));
        const allVersions = new Set();
        for (const pkgUrl of packageUrls) {
            const res = await (0, util_1.downloadHttpProtocol)(this.http, pkgUrl);
            // istanbul ignore if
            if (!res) {
                invalidPackageUrls.add(pkgUrl);
                continue;
            }
            const rootPath = new URL(pkgUrl).pathname;
            const versions = (0, util_2.extractPageLinks)(res.body, (href) => {
                const path = href.replace(rootPath, '');
                if (path.startsWith('.')) {
                    return null;
                }
                return path;
            });
            for (const version of versions) {
                allVersions.add(version);
            }
        }
        if (invalidPackageUrls.size > 0) {
            await packageCache.set('datasource-sbt-package', invalidPackageUrlsKey, [...invalidPackageUrls], 30 * 24 * 60);
        }
        if (packageUrls.length > 0) {
            const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`;
            await packageCache.set('datasource-sbt-package', packageUrlsKey, packageUrls, 30 * 24 * 60);
        }
        const versions = [...allVersions];
        if (!versions.length) {
            return null;
        }
        const releases = [...allVersions]
            .sort(compare_1.compare)
            .map((version) => ({ version }));
        const res = { releases, dependencyUrl };
        const latestVersion = (0, util_2.getLatestVersion)(versions);
        const pomInfo = await this.getPomInfo(registryUrl, packageName, latestVersion, packageUrls);
        if (pomInfo?.homepage) {
            res.homepage = pomInfo.homepage;
        }
        if (pomInfo?.sourceUrl) {
            res.sourceUrl = pomInfo.sourceUrl;
        }
        return res;
    }
    async getPomInfo(registryUrl, packageName, version, pkgUrls) {
        const packageUrlsKey = `package-urls:${registryUrl}:${packageName}`;
        // istanbul ignore next: will be covered later
        const packageUrls = pkgUrls ??
            (await packageCache.get('datasource-sbt-package', packageUrlsKey));
        // istanbul ignore if
        if (!packageUrls?.length) {
            return null;
        }
        // istanbul ignore if
        if (!version) {
            return null;
        }
        const invalidPomFilesKey = `invalid-pom-files:${registryUrl}:${packageName}:${version}`;
        const invalidPomFiles = new Set(await packageCache.get('datasource-sbt-package', invalidPomFilesKey));
        const saveCache = async () => {
            if (invalidPomFiles.size > 0) {
                await packageCache.set('datasource-sbt-package', invalidPomFilesKey, [...invalidPomFiles], 30 * 24 * 60);
            }
        };
        for (const packageUrl of packageUrls) {
            const artifactDir = upath.basename(packageUrl);
            const [artifact] = artifactDir.split('_');
            for (const pomFilePrefix of [artifactDir, artifact]) {
                const pomFileName = `${pomFilePrefix}-${version}.pom`;
                const pomUrl = `${packageUrl}${version}/${pomFileName}`;
                if (invalidPomFiles.has(pomUrl)) {
                    continue;
                }
                const res = await (0, util_1.downloadHttpProtocol)(this.http, pomUrl);
                const content = res?.body;
                if (!content) {
                    invalidPomFiles.add(pomUrl);
                    continue;
                }
                const result = {};
                const releaseTimestamp = (0, metadata_1.normalizeDate)(res.headers['last-modified']);
                if (releaseTimestamp) {
                    result.releaseTimestamp = releaseTimestamp;
                }
                const pomXml = new xmldoc_1.XmlDocument(content);
                const homepage = pomXml.valueWithPath('url');
                if (homepage) {
                    result.homepage = homepage;
                }
                const sourceUrl = pomXml.valueWithPath('scm.url');
                if (sourceUrl) {
                    result.sourceUrl = sourceUrl
                        .replace((0, regex_1.regEx)(/^scm:/), '')
                        .replace((0, regex_1.regEx)(/^git:/), '')
                        .replace((0, regex_1.regEx)(/^git@github.com:/), 'https://github.com/')
                        .replace((0, regex_1.regEx)(/\.git$/), '');
                }
                await saveCache();
                return result;
            }
        }
        await saveCache();
        return null;
    }
    async getReleases(config) {
        const { packageName, registryUrl } = config;
        // istanbul ignore if
        if (!registryUrl) {
            return null;
        }
        const sbtReleases = await this.getSbtReleases(registryUrl, packageName);
        if (sbtReleases) {
            return sbtReleases;
        }
        logger_1.logger.debug(`Sbt: no versions discovered for ${packageName} listing organization root package folder, fallback to maven datasource for version discovery`);
        const mavenReleaseResult = await super.getReleases(config);
        if (mavenReleaseResult) {
            return mavenReleaseResult;
        }
        logger_1.logger.debug(`Sbt: no versions found for "${packageName}"`);
        return null;
    }
    async postprocessRelease(config, release) {
        // istanbul ignore if
        if (!config.registryUrl) {
            return release;
        }
        const res = await this.getPomInfo(config.registryUrl, config.packageName, release.version);
        if (res?.releaseTimestamp) {
            release.releaseTimestamp = res.releaseTimestamp;
        }
        return release;
    }
}
exports.SbtPackageDatasource = SbtPackageDatasource;
tslib_1.__decorate([
    (0, decorator_1.cache)({
        namespace: 'datasource-sbt-package',
        key: ({ registryUrl, packageName }, { version }) => `postprocessRelease:${registryUrl}:${packageName}:${version}`,
        ttlMinutes: 30 * 24 * 60,
    })
], SbtPackageDatasource.prototype, "postprocessRelease", null);
//# sourceMappingURL=index.js.map