"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractAllPackageFiles = exports.extractPackageFile = exports.extractProxyUrls = void 0;
const tslib_1 = require("tslib");
const good_enough_parser_1 = require("good-enough-parser");
const logger_1 = require("../../../logger");
const fs_1 = require("../../../util/fs");
const regex_1 = require("../../../util/regex");
const url_1 = require("../../../util/url");
const github_releases_1 = require("../../datasource/github-releases");
const maven_1 = require("../../datasource/maven");
const sbt_package_1 = require("../../datasource/sbt-package");
const sbt_plugin_1 = require("../../datasource/sbt-plugin");
const versioning_1 = require("../../versioning");
const mavenVersioning = tslib_1.__importStar(require("../../versioning/maven"));
const semverVersioning = tslib_1.__importStar(require("../../versioning/semver"));
const util_1 = require("./util");
const SBT_MVN_REPO = 'https://repo1.maven.org/maven2';
const scala = good_enough_parser_1.lang.createLang('scala');
const sbtVersionRegex = (0, regex_1.regEx)('sbt\\.version *= *(?<version>\\d+\\.\\d+\\.\\d+)');
const sbtProxyUrlRegex = (0, regex_1.regEx)(/^\s*(?<repoName>\S+):\s+(?<proxy>https?:\/\/[\w./-]+)/);
const scalaVersionMatch = good_enough_parser_1.query
    .sym('scalaVersion')
    .op(':=')
    .alt(good_enough_parser_1.query.str((ctx, { value: scalaVersion }) => ({ ...ctx, scalaVersion })), good_enough_parser_1.query.sym((ctx, { value: varName }) => {
    const scalaVersion = ctx.vars[varName];
    if (scalaVersion) {
        ctx.scalaVersion = scalaVersion;
    }
    return ctx;
}))
    .handler((ctx) => {
    if (ctx.scalaVersion) {
        const version = (0, versioning_1.get)(mavenVersioning.id);
        let packageName = 'org.scala-lang:scala-library';
        if (version.getMajor(ctx.scalaVersion) === 3) {
            packageName = 'org.scala-lang:scala3-library_3';
        }
        const dep = {
            datasource: maven_1.MavenDatasource.id,
            depName: 'scala',
            packageName,
            currentValue: ctx.scalaVersion,
            separateMinorPatch: true,
        };
        ctx.scalaVersion = (0, util_1.normalizeScalaVersion)(ctx.scalaVersion);
        ctx.deps.push(dep);
    }
    return ctx;
});
const packageFileVersionMatch = good_enough_parser_1.query
    .sym('version')
    .op(':=')
    .alt(good_enough_parser_1.query.str((ctx, { value: packageFileVersion }) => ({
    ...ctx,
    packageFileVersion,
})), good_enough_parser_1.query.sym((ctx, { value: varName }) => {
    const packageFileVersion = ctx.vars[varName];
    if (packageFileVersion) {
        ctx.packageFileVersion = packageFileVersion;
    }
    return ctx;
}));
const variableNameMatch = good_enough_parser_1.query
    .sym((ctx, { value: varName }) => ({
    ...ctx,
    currentVarName: varName,
}))
    .opt(good_enough_parser_1.query.op(':').sym('String'));
const variableValueMatch = good_enough_parser_1.query.str((ctx, { value }) => {
    ctx.vars[ctx.currentVarName] = value;
    delete ctx.currentVarName;
    return ctx;
});
const assignmentMatch = good_enough_parser_1.query.sym('val').join(variableNameMatch).op('=');
const variableDefinitionMatch = good_enough_parser_1.query
    .alt(good_enough_parser_1.query.sym('lazy').join(assignmentMatch), assignmentMatch, variableNameMatch.op(':='))
    .join(variableValueMatch);
const groupIdMatch = good_enough_parser_1.query.alt(good_enough_parser_1.query.sym((ctx, { value: varName }) => {
    const currentGroupId = ctx.vars[varName];
    if (currentGroupId) {
        ctx.groupId = currentGroupId;
    }
    return ctx;
}), good_enough_parser_1.query.str((ctx, { value: groupId }) => ({ ...ctx, groupId })));
const artifactIdMatch = good_enough_parser_1.query.alt(good_enough_parser_1.query.sym((ctx, { value: varName }) => {
    const artifactId = ctx.vars[varName];
    if (artifactId) {
        ctx.artifactId = artifactId;
    }
    return ctx;
}), good_enough_parser_1.query.str((ctx, { value: artifactId }) => ({ ...ctx, artifactId })));
const versionMatch = good_enough_parser_1.query.alt(good_enough_parser_1.query.sym((ctx, { value: varName }) => {
    const currentValue = ctx.vars[varName];
    if (currentValue) {
        ctx.currentValue = currentValue;
        ctx.variableName = varName;
    }
    return ctx;
}), good_enough_parser_1.query.str((ctx, { value: currentValue }) => ({ ...ctx, currentValue })));
const simpleDependencyMatch = groupIdMatch
    .op('%')
    .join(artifactIdMatch)
    .op('%')
    .join(versionMatch);
const versionedDependencyMatch = groupIdMatch
    .op('%%')
    .join(artifactIdMatch)
    .handler((ctx) => ({ ...ctx, useScalaVersion: true }))
    .op('%')
    .join(versionMatch);
const crossDependencyMatch = groupIdMatch
    .op('%%%')
    .join(artifactIdMatch)
    .handler((ctx) => ({ ...ctx, useScalaVersion: true }))
    .op('%')
    .join(versionMatch);
function depHandler(ctx) {
    const { scalaVersion, groupId, artifactId, currentValue, useScalaVersion, depType, variableName, } = ctx;
    delete ctx.groupId;
    delete ctx.artifactId;
    delete ctx.currentValue;
    delete ctx.useScalaVersion;
    delete ctx.depType;
    delete ctx.variableName;
    const depName = `${groupId}:${artifactId}`;
    const dep = {
        datasource: sbt_package_1.SbtPackageDatasource.id,
        depName,
        packageName: scalaVersion && useScalaVersion ? `${depName}_${scalaVersion}` : depName,
        currentValue,
    };
    if (depType) {
        dep.depType = depType;
    }
    if (depType === 'plugin') {
        dep.datasource = sbt_plugin_1.SbtPluginDatasource.id;
    }
    if (variableName) {
        dep.groupName = variableName;
        dep.variableName = variableName;
    }
    ctx.deps.push(dep);
    return ctx;
}
function depTypeHandler(ctx, { value: depType }) {
    return { ...ctx, depType };
}
const sbtPackageMatch = good_enough_parser_1.query
    .opt(good_enough_parser_1.query.opt(good_enough_parser_1.query.sym('lazy')).sym('val').sym().op('='))
    .alt(crossDependencyMatch, simpleDependencyMatch, versionedDependencyMatch)
    .opt(good_enough_parser_1.query.alt(good_enough_parser_1.query.sym('classifier').str(depTypeHandler), good_enough_parser_1.query.op('%').sym(depTypeHandler), good_enough_parser_1.query.op('%').str(depTypeHandler)))
    .handler(depHandler);
const sbtPluginMatch = good_enough_parser_1.query
    .sym((0, regex_1.regEx)(/^(?:addSbtPlugin|addCompilerPlugin)$/))
    .tree({
    type: 'wrapped-tree',
    maxDepth: 1,
    search: good_enough_parser_1.query
        .begin()
        .alt(simpleDependencyMatch, versionedDependencyMatch)
        .end(),
})
    .handler((ctx) => ({ ...ctx, depType: 'plugin' }))
    .handler(depHandler);
const resolverMatch = good_enough_parser_1.query
    .str()
    .sym('at')
    .str((ctx, { value }) => {
    if ((0, url_1.parseUrl)(value)) {
        ctx.registryUrls.push(value);
    }
    return ctx;
});
const addResolverMatch = good_enough_parser_1.query.sym('resolvers').alt(good_enough_parser_1.query.op('+=').join(resolverMatch), good_enough_parser_1.query.op('++=').sym('Seq').tree({
    type: 'wrapped-tree',
    maxDepth: 1,
    search: resolverMatch,
}));
function registryUrlHandler(ctx) {
    for (const dep of ctx.deps) {
        dep.registryUrls = [...ctx.registryUrls];
    }
    return ctx;
}
const query = good_enough_parser_1.query.tree({
    type: 'root-tree',
    maxDepth: 32,
    search: good_enough_parser_1.query.alt(scalaVersionMatch, packageFileVersionMatch, sbtPackageMatch, sbtPluginMatch, addResolverMatch, variableDefinitionMatch),
    postHandler: registryUrlHandler,
});
function extractProxyUrls(content, packageFile) {
    const extractedProxyUrls = [];
    logger_1.logger.debug(`Parsing proxy repository file ${packageFile}`);
    for (const line of content.split(regex_1.newlineRegex)) {
        const extraction = sbtProxyUrlRegex.exec(line);
        if (extraction?.groups?.proxy) {
            extractedProxyUrls.push(extraction.groups.proxy);
        }
        else if (line.trim() === 'maven-central') {
            extractedProxyUrls.push(SBT_MVN_REPO);
        }
    }
    return extractedProxyUrls;
}
exports.extractProxyUrls = extractProxyUrls;
function extractPackageFile(content, packageFile) {
    if (packageFile === 'project/build.properties' ||
        packageFile.endsWith('/project/build.properties')) {
        const regexResult = sbtVersionRegex.exec(content);
        const sbtVersion = regexResult?.groups?.version;
        const matchString = regexResult?.[0];
        if (sbtVersion) {
            const sbtDependency = {
                datasource: github_releases_1.GithubReleasesDatasource.id,
                depName: 'sbt/sbt',
                packageName: 'sbt/sbt',
                versioning: semverVersioning.id,
                currentValue: sbtVersion,
                replaceString: matchString,
                extractVersion: '^v(?<version>\\S+)',
                registryUrls: [],
            };
            return {
                deps: [sbtDependency],
            };
        }
        else {
            return null;
        }
    }
    let parsedResult = null;
    try {
        parsedResult = scala.query(content, query, {
            vars: {},
            deps: [],
            registryUrls: [],
        });
    }
    catch (err) /* istanbul ignore next */ {
        logger_1.logger.debug({ err, packageFile }, 'Sbt parsing error');
    }
    if (!parsedResult) {
        return null;
    }
    const { deps, packageFileVersion } = parsedResult;
    if (!deps.length) {
        return null;
    }
    return { deps, packageFileVersion };
}
exports.extractPackageFile = extractPackageFile;
async function extractAllPackageFiles(_config, packageFiles) {
    const packages = [];
    const proxyUrls = [];
    for (const packageFile of packageFiles) {
        const content = await (0, fs_1.readLocalFile)(packageFile, 'utf8');
        if (!content) {
            logger_1.logger.debug({ packageFile }, 'packageFile has no content');
            continue;
        }
        if (packageFile === 'repositories') {
            const urls = extractProxyUrls(content, packageFile);
            proxyUrls.push(...urls);
        }
        else {
            const pkg = extractPackageFile(content, packageFile);
            if (pkg) {
                packages.push({ deps: pkg.deps, packageFile });
            }
        }
    }
    for (const pkg of packages) {
        for (const dep of pkg.deps) {
            if (dep.datasource !== github_releases_1.GithubReleasesDatasource.id) {
                if (proxyUrls.length > 0) {
                    dep.registryUrls.unshift(...proxyUrls);
                }
                else if (dep.depType === 'plugin') {
                    dep.registryUrls.unshift(sbt_plugin_1.SBT_PLUGINS_REPO, SBT_MVN_REPO);
                }
                else {
                    dep.registryUrls.unshift(SBT_MVN_REPO);
                }
            }
        }
    }
    return packages;
}
exports.extractAllPackageFiles = extractAllPackageFiles;
//# sourceMappingURL=extract.js.map