"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComposerExtract = exports.Lockfile = exports.PackageFile = exports.Repos = exports.ReposArray = exports.ReposRecord = exports.NamedRepo = exports.Repo = exports.PackageRepo = exports.PathRepo = exports.GitRepo = exports.ComposerRepo = void 0;
const zod_1 = require("zod");
const logger_1 = require("../../../logger");
const fs_1 = require("../../../util/fs");
const regex_1 = require("../../../util/regex");
const schema_utils_1 = require("../../../util/schema-utils");
const bitbucket_tags_1 = require("../../datasource/bitbucket-tags");
const git_tags_1 = require("../../datasource/git-tags");
const github_tags_1 = require("../../datasource/github-tags");
const packagist_1 = require("../../datasource/packagist");
const composer_1 = require("../../versioning/composer");
exports.ComposerRepo = zod_1.z.object({
    type: zod_1.z.literal('composer'),
    /**
     * The regUrl is expected to be a base URL. GitLab composer repository installation guide specifies
     * to use a base URL containing packages.json. Composer still works in this scenario by determining
     * whether to add / remove packages.json from the URL.
     *
     * See https://github.com/composer/composer/blob/750a92b4b7aecda0e5b2f9b963f1cb1421900675/src/Composer/Repository/ComposerRepository.php#L815
     */
    url: zod_1.z.string().transform((url) => url.replace(/\/packages\.json$/, '')),
});
exports.GitRepo = zod_1.z.object({
    type: zod_1.z.enum(['vcs', 'git']).transform(() => 'git'),
    url: zod_1.z.string(),
    name: zod_1.z.string().optional(),
});
exports.PathRepo = zod_1.z.object({
    type: zod_1.z.literal('path'),
    url: zod_1.z.string(),
    name: zod_1.z.string().optional(),
});
exports.PackageRepo = zod_1.z.object({
    type: zod_1.z.literal('package'),
});
exports.Repo = zod_1.z.discriminatedUnion('type', [
    exports.ComposerRepo,
    exports.GitRepo,
    exports.PathRepo,
    exports.PackageRepo,
]);
exports.NamedRepo = zod_1.z.discriminatedUnion('type', [
    exports.ComposerRepo,
    exports.GitRepo.extend({ name: zod_1.z.string() }),
    exports.PathRepo.extend({ name: zod_1.z.string() }),
    exports.PackageRepo,
]);
const DisablePackagist = zod_1.z.object({ type: zod_1.z.literal('disable-packagist') });
const bitbucketUrlRegex = (0, regex_1.regEx)(/^(?:https:\/\/|git@)bitbucket\.org[/:](?<packageName>[^/]+\/[^/]+?)(?:\.git)?$/);
exports.ReposRecord = (0, schema_utils_1.LooseRecord)(zod_1.z.union([exports.Repo, zod_1.z.literal(false)]), {
    onError: ({ error: err }) => {
        logger_1.logger.debug({ err }, 'Composer: error parsing repositories object');
    },
}).transform((repos) => {
    const result = [];
    for (const [name, repo] of Object.entries(repos)) {
        if (repo === false) {
            if (name === 'packagist' || name === 'packagist.org') {
                result.push({ type: 'disable-packagist' });
            }
            continue;
        }
        if (repo.type === 'path' || repo.type === 'git') {
            result.push({ name, ...repo });
            continue;
        }
        if (repo.type === 'composer') {
            result.push(repo);
            continue;
        }
    }
    return result;
});
exports.ReposArray = (0, schema_utils_1.LooseArray)(zod_1.z.union([
    exports.Repo,
    zod_1.z
        .union([
        zod_1.z.object({ packagist: zod_1.z.literal(false) }),
        zod_1.z.object({ 'packagist.org': zod_1.z.literal(false) }),
    ])
        .transform(() => ({ type: 'disable-packagist' })),
]), {
    onError: ({ error: err }) => {
        logger_1.logger.debug({ err }, 'Composer: error parsing repositories array');
    },
}).transform((repos) => {
    const result = [];
    for (let idx = 0; idx < repos.length; idx++) {
        const repo = repos[idx];
        if (repo.type === 'path' || repo.type === 'git') {
            result.push({ name: `__${idx}`, ...repo });
        }
        else {
            result.push(repo);
        }
    }
    return result;
});
exports.Repos = zod_1.z
    .union([exports.ReposRecord, exports.ReposArray])
    .default([]) // Prevents warnings for packages without repositories field
    .catch(({ error: err }) => {
    logger_1.logger.debug({ err }, 'Composer: invalid "repositories" field');
    return [];
})
    .transform((repos) => {
    let packagist = true;
    const repoUrls = [];
    const gitRepos = {};
    const pathRepos = {};
    for (const repo of repos) {
        if (repo.type === 'composer') {
            repoUrls.push(repo.url);
        }
        else if (repo.type === 'git') {
            gitRepos[repo.name] = repo;
        }
        else if (repo.type === 'path') {
            pathRepos[repo.name] = repo;
        }
        else if (repo.type === 'disable-packagist') {
            packagist = false;
        }
    }
    if (packagist && repoUrls.length) {
        repoUrls.push('https://packagist.org');
    }
    const registryUrls = repoUrls.length ? repoUrls : null;
    return { registryUrls, gitRepos, pathRepos };
});
const RequireDefs = (0, schema_utils_1.LooseRecord)(zod_1.z.string().transform((x) => x.trim())).catch({});
exports.PackageFile = zod_1.z
    .object({
    type: zod_1.z.string().optional(),
    config: zod_1.z
        .object({
        platform: zod_1.z.object({
            php: zod_1.z.string(),
        }),
    })
        .nullable()
        .catch(null),
    repositories: exports.Repos,
    require: RequireDefs,
    'require-dev': RequireDefs,
})
    .transform(({ type: composerJsonType, config, repositories, require, 'require-dev': requireDev, }) => ({
    composerJsonType,
    config,
    repositories,
    require,
    requireDev,
}));
const LockedPackage = zod_1.z.object({
    name: zod_1.z.string(),
    version: zod_1.z.string(),
});
exports.Lockfile = zod_1.z
    .object({
    'plugin-api-version': zod_1.z.string().optional(),
    packages: (0, schema_utils_1.LooseArray)(LockedPackage).catch([]),
    'packages-dev': (0, schema_utils_1.LooseArray)(LockedPackage).catch([]),
})
    .transform(({ 'plugin-api-version': pluginApiVersion, packages, 'packages-dev': packagesDev, }) => ({ pluginApiVersion, packages, packagesDev }));
exports.ComposerExtract = zod_1.z
    .object({
    content: zod_1.z.string(),
    fileName: zod_1.z.string(),
})
    .transform(({ content, fileName }) => {
    const lockfileName = fileName.replace(/\.json$/, '.lock');
    return {
        file: content,
        lockfileName,
        lockfile: lockfileName,
    };
})
    .pipe(zod_1.z.object({
    file: schema_utils_1.Json.pipe(exports.PackageFile),
    lockfileName: zod_1.z.string(),
    lockfile: zod_1.z
        .string()
        .transform((lockfileName) => (0, fs_1.readLocalFile)(lockfileName, 'utf8'))
        .pipe(zod_1.z.union([
        zod_1.z.null(),
        zod_1.z
            .string()
            .pipe(schema_utils_1.Json)
            .pipe(exports.Lockfile)
            .nullable()
            .catch(({ error: err }) => {
            logger_1.logger.debug({ err }, 'Composer: lockfile parsing error');
            return null;
        }),
    ])),
}))
    .transform(({ file, lockfile, lockfileName }) => {
    const { composerJsonType, require, requireDev } = file;
    const { registryUrls, gitRepos, pathRepos } = file.repositories;
    const deps = [];
    const profiles = [
        {
            depType: 'require',
            req: require,
            locked: lockfile?.packages ?? [],
        },
        {
            depType: 'require-dev',
            req: requireDev,
            locked: lockfile?.packagesDev ?? [],
        },
    ];
    for (const { depType, req, locked } of profiles) {
        for (const [depName, currentValue] of Object.entries(req)) {
            if (depName === 'php') {
                deps.push({
                    depType,
                    depName,
                    currentValue,
                    datasource: github_tags_1.GithubTagsDatasource.id,
                    packageName: 'containerbase/php-prebuild',
                });
                continue;
            }
            if (pathRepos[depName]) {
                deps.push({
                    depType,
                    depName,
                    currentValue,
                    skipReason: 'path-dependency',
                });
                continue;
            }
            const dep = {
                depType,
                depName,
                currentValue,
            };
            if (!depName.includes('/')) {
                dep.skipReason = 'unsupported';
            }
            const lockedDep = locked.find((item) => item.name === depName);
            if (lockedDep && composer_1.api.isVersion(lockedDep.version)) {
                dep.lockedVersion = lockedDep.version.replace((0, regex_1.regEx)(/^v/i), '');
            }
            const gitRepo = gitRepos[depName];
            if (gitRepo) {
                const bitbucketMatchGroups = bitbucketUrlRegex.exec(gitRepo.url)
                    ?.groups;
                if (bitbucketMatchGroups) {
                    dep.datasource = bitbucket_tags_1.BitbucketTagsDatasource.id;
                    dep.packageName = bitbucketMatchGroups.packageName;
                    deps.push(dep);
                    continue;
                }
                dep.datasource = git_tags_1.GitTagsDatasource.id;
                dep.packageName = gitRepo.url;
                deps.push(dep);
                continue;
            }
            dep.datasource = packagist_1.PackagistDatasource.id;
            if (registryUrls) {
                dep.registryUrls = registryUrls;
            }
            deps.push(dep);
        }
    }
    if (!deps.length) {
        return null;
    }
    const res = { deps };
    if (composerJsonType) {
        res.managerData = { composerJsonType };
    }
    if (require.php) {
        res.extractedConstraints = { php: require.php };
    }
    if (lockfile) {
        res.lockFiles = [lockfileName];
    }
    return res;
});
//# sourceMappingURL=schema.js.map