"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("@pnpm/constants");
const lockfile_file_1 = require("@pnpm/lockfile-file");
const lockfile_utils_1 = require("@pnpm/lockfile-utils");
const types_1 = require("@pnpm/types");
const dp = require("dependency-path");
const micromatch_1 = require("micromatch");
async function default_1(opts) {
    return _outdated([], opts);
}
exports.default = default_1;
async function forPackages(packages, opts) {
    return _outdated(packages, opts);
}
exports.forPackages = forPackages;
async function _outdated(forPkgs, opts) {
    if (packageHasNoDeps(opts.manifest))
        return [];
    const importerId = lockfile_file_1.getLockfileImporterId(opts.lockfileDirectory, opts.prefix);
    const currentLockfile = opts.currentLockfile || { importers: { [importerId]: {} } };
    const outdated = [];
    await Promise.all(types_1.DEPENDENCIES_FIELDS.map(async (depType) => {
        if (!opts.wantedLockfile.importers[importerId][depType])
            return;
        let pkgs = Object.keys(opts.wantedLockfile.importers[importerId][depType]);
        if (forPkgs.length) {
            pkgs = pkgs.filter((pkgName) => forPkgs.some((forPkg) => micromatch_1.isMatch(pkgName, forPkg)));
        }
        await Promise.all(pkgs.map(async (alias) => {
            const ref = opts.wantedLockfile.importers[importerId][depType][alias];
            // ignoring linked packages. (For backward compatibility)
            if (ref.startsWith('file:')) {
                return;
            }
            const relativeDepPath = dp.refToRelative(ref, alias);
            // ignoring linked packages
            if (relativeDepPath === null)
                return;
            const pkgSnapshot = opts.wantedLockfile.packages && opts.wantedLockfile.packages[relativeDepPath];
            if (!pkgSnapshot) {
                throw new Error(`Invalid ${constants_1.WANTED_LOCKFILE} file. ${relativeDepPath} not found in packages field`);
            }
            const currentRef = currentLockfile.importers[importerId] &&
                currentLockfile.importers[importerId][depType] &&
                currentLockfile.importers[importerId][depType][alias];
            const currentRelative = currentRef && dp.refToRelative(currentRef, alias);
            const current = currentRelative && dp.parse(currentRelative).version || currentRef;
            const wanted = dp.parse(relativeDepPath).version || ref;
            const packageName = lockfile_utils_1.nameVerFromPkgSnapshot(relativeDepPath, pkgSnapshot).name;
            // It might be not the best solution to check for pkgSnapshot.name
            // TODO: add some other field to distinct packages not from the registry
            if (pkgSnapshot.resolution && (pkgSnapshot.resolution['type'] || pkgSnapshot.name)) { // tslint:disable-line:no-string-literal
                if (current !== wanted) {
                    outdated.push({
                        alias,
                        belongsTo: depType,
                        current,
                        latestManifest: undefined,
                        packageName,
                        wanted,
                    });
                }
                return;
            }
            const latestManifest = await opts.getLatestManifest(dp.parse(relativeDepPath).name || packageName);
            if (!latestManifest)
                return;
            if (!current) {
                outdated.push({
                    alias,
                    belongsTo: depType,
                    latestManifest,
                    packageName,
                    wanted,
                });
                return;
            }
            if (current !== wanted || latestManifest.version !== current || latestManifest.deprecated) {
                outdated.push({
                    alias,
                    belongsTo: depType,
                    current,
                    latestManifest,
                    packageName,
                    wanted,
                });
            }
        }));
    }));
    return outdated.sort((pkg1, pkg2) => pkg1.packageName.localeCompare(pkg2.packageName));
}
function packageHasNoDeps(manifest) {
    return (!manifest.dependencies || isEmpty(manifest.dependencies))
        && (!manifest.devDependencies || isEmpty(manifest.devDependencies))
        && (!manifest.optionalDependencies || isEmpty(manifest.optionalDependencies));
}
function isEmpty(obj) {
    return Object.keys(obj).length === 0;
}
