"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@pnpm/utils");
const resolveDependencies_1 = require("./resolveDependencies");
async function default_1(importers, opts) {
    const directNonLinkedDepsByImporterId = {};
    const linkedDependenciesByImporterId = {};
    const wantedToBeSkippedPackageIds = new Set();
    const ctx = {
        childrenByParentId: {},
        currentLockfile: opts.currentLockfile,
        defaultTag: opts.tag,
        dependenciesTree: {},
        dryRun: opts.dryRun,
        engineStrict: opts.engineStrict,
        force: opts.force,
        lockfileDirectory: opts.lockfileDirectory,
        nodeVersion: opts.nodeVersion,
        outdatedDependencies: {},
        pendingNodes: [],
        pnpmVersion: opts.pnpmVersion,
        readPackageHook: opts.hooks.readPackage,
        registries: opts.registries,
        resolvedPackagesByPackageId: {},
        sideEffectsCache: opts.sideEffectsCache,
        skipped: wantedToBeSkippedPackageIds,
        storeController: opts.storeController,
        updateLockfile: opts.updateLockfile,
        virtualStoreDir: opts.virtualStoreDir,
        wantedLockfile: opts.wantedLockfile,
    };
    await Promise.all(importers.map(async (importer) => {
        const lockfileImporter = opts.wantedLockfile.importers[importer.id];
        // This array will only contain the dependencies that should be linked in.
        // The already linked-in dependencies will not be added.
        const linkedDependencies = [];
        const resolveCtx = Object.assign(Object.assign({}, ctx), { linkedDependencies, modulesDir: importer.modulesDir, prefix: importer.prefix, resolutionStrategy: opts.resolutionStrategy || 'fast' });
        const resolveOpts = {
            currentDepth: 0,
            localPackages: opts.localPackages,
            parentDependsOnPeers: true,
            parentNodeId: `>${importer.id}>`,
            preferredVersions: importer.preferredVersions || {},
            proceed: true,
            resolvedDependencies: Object.assign(Object.assign(Object.assign({}, lockfileImporter.dependencies), lockfileImporter.devDependencies), lockfileImporter.optionalDependencies),
            updateDepth: -1,
        };
        directNonLinkedDepsByImporterId[importer.id] = await resolveDependencies_1.default(resolveCtx, importer.wantedDependencies, resolveOpts);
        linkedDependenciesByImporterId[importer.id] = linkedDependencies;
    }));
    ctx.pendingNodes.forEach((pendingNode) => {
        ctx.dependenciesTree[pendingNode.nodeId] = {
            children: () => buildTree(ctx, pendingNode.nodeId, pendingNode.resolvedPackage.id, ctx.childrenByParentId[pendingNode.resolvedPackage.id], pendingNode.depth + 1, pendingNode.installable),
            depth: pendingNode.depth,
            installable: pendingNode.installable,
            resolvedPackage: pendingNode.resolvedPackage,
        };
    });
    const resolvedImporters = {};
    for (const { id } of importers) {
        const directNonLinkedDeps = directNonLinkedDepsByImporterId[id];
        const linkedDependencies = linkedDependenciesByImporterId[id];
        resolvedImporters[id] = {
            directDependencies: [
                ...directNonLinkedDeps
                    .map(({ alias, nodeId, normalizedPref, specRaw }) => (Object.assign(Object.assign({}, ctx.dependenciesTree[nodeId].resolvedPackage), { alias,
                    normalizedPref,
                    specRaw }))),
                ...linkedDependencies,
            ],
            directNodeIdsByAlias: directNonLinkedDeps
                .reduce((acc, dependency) => {
                acc[dependency.alias] = dependency.nodeId;
                return acc;
            }, {}),
            linkedDependencies,
        };
    }
    return {
        dependenciesTree: ctx.dependenciesTree,
        outdatedDependencies: ctx.outdatedDependencies,
        resolvedImporters,
        resolvedPackagesByPackageId: ctx.resolvedPackagesByPackageId,
        wantedToBeSkippedPackageIds,
    };
}
exports.default = default_1;
function buildTree(ctx, parentNodeId, parentId, children, depth, installable) {
    const childrenNodeIds = {};
    for (const child of children) {
        if (utils_1.nodeIdContainsSequence(parentNodeId, parentId, child.pkgId)) {
            continue;
        }
        const childNodeId = utils_1.createNodeId(parentNodeId, child.pkgId);
        childrenNodeIds[child.alias] = childNodeId;
        installable = installable && !ctx.skipped.has(child.pkgId);
        ctx.dependenciesTree[childNodeId] = {
            children: () => buildTree(ctx, childNodeId, child.pkgId, ctx.childrenByParentId[child.pkgId], depth + 1, installable),
            depth,
            installable,
            resolvedPackage: ctx.resolvedPackagesByPackageId[child.pkgId],
        };
    }
    return childrenNodeIds;
}
