"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPackageFile = extractPackageFile;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const logger_1 = require("../../../logger");
const fs_1 = require("../../../util/fs");
const regex_1 = require("../../../util/regex");
const ruby_version_1 = require("../../datasource/ruby-version");
const rubygems_1 = require("../../datasource/rubygems");
const common_1 = require("./common");
const locked_version_1 = require("./locked-version");
function formatContent(input) {
    return input.replace((0, regex_1.regEx)(/^ {2}/), '') + '\n'; //remove leading whitespace and add a new line at the end
}
async function extractPackageFile(content, packageFile) {
    let lineNumber;
    async function processGroupBlock(line, repositoryUrl, trimGroupLine = false) {
        const groupMatch = (0, regex_1.regEx)(/^group\s+(.*?)\s+do/).exec(line);
        if (groupMatch) {
            const depTypes = groupMatch[1]
                .split(',')
                .map((group) => group.trim())
                .map((group) => group.replace((0, regex_1.regEx)(/^:/), ''));
            const groupLineNumber = lineNumber;
            let groupContent = '';
            let groupLine = '';
            while (lineNumber < lines.length &&
                (trimGroupLine ? groupLine.trim() !== 'end' : groupLine !== 'end')) {
                lineNumber += 1;
                groupLine = lines[lineNumber];
                // istanbul ignore if
                if (!is_1.default.string(groupLine)) {
                    logger_1.logger.debug({ content, packageFile, type: 'groupLine' }, 'Bundler parsing error');
                    groupLine = 'end';
                }
                if (trimGroupLine ? groupLine.trim() !== 'end' : groupLine !== 'end') {
                    groupContent += formatContent(groupLine);
                }
            }
            const groupRes = await extractPackageFile(groupContent);
            if (groupRes) {
                res.deps = res.deps.concat(groupRes.deps.map((dep) => {
                    const depObject = {
                        ...dep,
                        depTypes,
                        managerData: {
                            lineNumber: Number(dep.managerData?.lineNumber) + groupLineNumber + 1,
                        },
                    };
                    if (repositoryUrl) {
                        depObject.registryUrls = [repositoryUrl];
                    }
                    return depObject;
                }));
            }
        }
    }
    const res = {
        registryUrls: [],
        deps: [],
    };
    const variables = {};
    const lines = content.split(regex_1.newlineRegex);
    for (lineNumber = 0; lineNumber < lines.length; lineNumber += 1) {
        const line = lines[lineNumber];
        let sourceMatch = null;
        for (const delimiter of common_1.delimiters) {
            sourceMatch =
                sourceMatch ??
                    (0, regex_1.regEx)(`^source ((${delimiter}(?<registryUrl>[^${delimiter}]+)${delimiter})|(?<sourceName>\\w+))\\s*$`).exec(line);
        }
        if (sourceMatch) {
            if (sourceMatch.groups?.registryUrl) {
                res.registryUrls?.push(sourceMatch.groups.registryUrl);
            }
            if (sourceMatch.groups?.sourceName) {
                const registryUrl = variables[sourceMatch.groups.sourceName];
                if (registryUrl) {
                    res.registryUrls?.push(registryUrl);
                }
            }
        }
        const rubyMatch = (0, common_1.extractRubyVersion)(line);
        if (rubyMatch) {
            res.deps.push({
                depName: 'ruby',
                currentValue: rubyMatch,
                datasource: ruby_version_1.RubyVersionDatasource.id,
                registryUrls: null,
            });
        }
        const variableMatchRegex = (0, regex_1.regEx)(`^(?<key>\\w+)\\s*=\\s*['"](?<value>[^'"]+)['"]`);
        const variableMatch = variableMatchRegex.exec(line);
        if (variableMatch) {
            if (variableMatch.groups?.key) {
                variables[variableMatch.groups?.key] = variableMatch.groups?.value;
            }
        }
        const gemMatchRegex = (0, regex_1.regEx)(`^\\s*gem\\s+(['"])(?<depName>[^'"]+)(['"])(\\s*,\\s*(?<currentValue>(['"])[^'"]+['"](\\s*,\\s*['"][^'"]+['"])?))?(\\s*,\\s*source:\\s*(['"](?<registryUrl>[^'"]+)['"]|(?<sourceName>[^'"]+)))?`);
        const gemMatch = gemMatchRegex.exec(line);
        if (gemMatch) {
            const dep = {
                depName: gemMatch.groups?.depName,
                managerData: { lineNumber },
            };
            if (gemMatch.groups?.currentValue) {
                const currentValue = gemMatch.groups.currentValue;
                dep.currentValue = currentValue;
            }
            dep.datasource = rubygems_1.RubygemsDatasource.id;
            res.deps.push(dep);
        }
        await processGroupBlock(line);
        for (const delimiter of common_1.delimiters) {
            const sourceBlockMatch = (0, regex_1.regEx)(`^source\\s+((${delimiter}(?<registryUrl>[^${delimiter}]+)${delimiter})|(?<sourceName>\\w+))\\s+do`).exec(line);
            if (sourceBlockMatch) {
                let repositoryUrl = '';
                if (sourceBlockMatch.groups?.registryUrl) {
                    repositoryUrl = sourceBlockMatch.groups.registryUrl;
                }
                if (sourceBlockMatch.groups?.sourceName) {
                    if (variables[sourceBlockMatch.groups.sourceName]) {
                        repositoryUrl = variables[sourceBlockMatch.groups.sourceName];
                    }
                }
                const sourceLineNumber = lineNumber;
                let sourceContent = '';
                let sourceLine = '';
                while (lineNumber < lines.length && sourceLine.trim() !== 'end') {
                    lineNumber += 1;
                    sourceLine = lines[lineNumber];
                    // istanbul ignore if
                    if (!is_1.default.string(sourceLine)) {
                        logger_1.logger.debug({ content, packageFile, type: 'sourceLine' }, 'Bundler parsing error');
                        sourceLine = 'end';
                    }
                    await processGroupBlock(sourceLine.trim(), repositoryUrl, true);
                    if (sourceLine.trim() !== 'end') {
                        sourceContent += formatContent(sourceLine);
                    }
                }
                const sourceRes = await extractPackageFile(sourceContent);
                if (sourceRes) {
                    res.deps = res.deps.concat(sourceRes.deps.map((dep) => ({
                        ...dep,
                        registryUrls: [repositoryUrl],
                        managerData: {
                            lineNumber: Number(dep.managerData?.lineNumber) + sourceLineNumber + 1,
                        },
                    })));
                }
            }
        }
        const platformsMatch = (0, regex_1.regEx)(/^platforms\s+(.*?)\s+do/).test(line);
        if (platformsMatch) {
            const platformsLineNumber = lineNumber;
            let platformsContent = '';
            let platformsLine = '';
            while (lineNumber < lines.length && platformsLine !== 'end') {
                lineNumber += 1;
                platformsLine = lines[lineNumber];
                // istanbul ignore if
                if (!is_1.default.string(platformsLine)) {
                    logger_1.logger.debug({ content, packageFile, type: 'platformsLine' }, 'Bundler parsing error');
                    platformsLine = 'end';
                }
                if (platformsLine !== 'end') {
                    platformsContent += formatContent(platformsLine);
                }
            }
            const platformsRes = await extractPackageFile(platformsContent);
            if (platformsRes) {
                res.deps = res.deps.concat(platformsRes.deps.map((dep) => ({
                    ...dep,
                    managerData: {
                        lineNumber: Number(dep.managerData?.lineNumber) + platformsLineNumber + 1,
                    },
                })));
            }
        }
        const ifMatch = (0, regex_1.regEx)(/^if\s+(.*?)/).test(line);
        if (ifMatch) {
            const ifLineNumber = lineNumber;
            let ifContent = '';
            let ifLine = '';
            while (lineNumber < lines.length && ifLine !== 'end') {
                lineNumber += 1;
                ifLine = lines[lineNumber];
                // istanbul ignore if
                if (!is_1.default.string(ifLine)) {
                    logger_1.logger.debug({ content, packageFile, type: 'ifLine' }, 'Bundler parsing error');
                    ifLine = 'end';
                }
                if (ifLine !== 'end') {
                    ifContent += formatContent(ifLine);
                }
            }
            const ifRes = await extractPackageFile(ifContent);
            if (ifRes) {
                res.deps = res.deps.concat(ifRes.deps.map((dep) => ({
                    ...dep,
                    managerData: {
                        lineNumber: Number(dep.managerData?.lineNumber) + ifLineNumber + 1,
                    },
                })));
            }
        }
    }
    if (!res.deps.length && !res.registryUrls?.length) {
        return null;
    }
    if (packageFile) {
        const gemfileLockPath = await (0, common_1.getLockFilePath)(packageFile);
        const lockContent = await (0, fs_1.readLocalFile)(gemfileLockPath, 'utf8');
        if (lockContent) {
            logger_1.logger.debug(`Found lock file ${gemfileLockPath} for packageFile: ${packageFile}`);
            res.lockFiles = [gemfileLockPath];
            const lockedEntries = (0, locked_version_1.extractLockFileEntries)(lockContent);
            for (const dep of res.deps) {
                // TODO: types (#22198)
                const lockedDepValue = lockedEntries.get(`${dep.depName}`);
                if (lockedDepValue) {
                    dep.lockedVersion = lockedDepValue;
                }
            }
        }
    }
    return res;
}
//# sourceMappingURL=extract.js.map