"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const p_limit_1 = require("p-limit");
const path = require("path");
const pathAbsolute = require("path-absolute");
const R = require("ramda");
const supi_1 = require("supi");
const createStoreController_1 = require("../createStoreController");
const findWorkspacePackages_1 = require("../findWorkspacePackages");
const getConfig_1 = require("../getConfig");
const getSaveType_1 = require("../getSaveType");
const readImporterManifest_1 = require("../readImporterManifest");
const installLimit = p_limit_1.default(4);
exports.default = async (input, opts) => {
    var _a, _b;
    const cwd = (_b = (_a = opts) === null || _a === void 0 ? void 0 : _a.dir, (_b !== null && _b !== void 0 ? _b : process.cwd()));
    const storeControllerCache = new Map();
    let workspacePackages;
    let localPackages;
    if (opts.linkWorkspacePackages && opts.workspaceDir) {
        workspacePackages = await findWorkspacePackages_1.default(opts.workspaceDir, opts);
        localPackages = findWorkspacePackages_1.arrayOfLocalPackagesToMap(workspacePackages);
    }
    else {
        localPackages = {};
    }
    const store = await createStoreController_1.cached(storeControllerCache, opts);
    const linkOpts = Object.assign(opts, {
        localPackages,
        storeController: store.ctrl,
        storeDir: store.dir,
        targetDependenciesField: getSaveType_1.default(opts),
    });
    // pnpm link
    if (!input || !input.length) {
        const { manifest, writeImporterManifest } = await readImporterManifest_1.tryReadImporterManifest(opts.globalDir, opts);
        const newManifest = await supi_1.linkToGlobal(cwd, {
            ...linkOpts,
            // A temporary workaround. global bin/prefix are always defined when --global is set
            globalBin: linkOpts.globalBin,
            globalDir: linkOpts.globalDir,
            manifest: manifest || {},
        });
        await writeImporterManifest(newManifest);
        return;
    }
    const [pkgPaths, pkgNames] = R.partition((inp) => inp.startsWith('.'), input);
    if (pkgNames.length) {
        let globalPkgNames;
        if (opts.workspaceDir) {
            workspacePackages = await findWorkspacePackages_1.default(opts.workspaceDir, opts);
            const pkgsFoundInWorkspace = workspacePackages.filter((pkg) => pkgNames.includes(pkg.manifest.name));
            pkgsFoundInWorkspace.forEach((pkgFromWorkspace) => pkgPaths.push(pkgFromWorkspace.dir));
            if (pkgsFoundInWorkspace.length && !linkOpts.targetDependenciesField) {
                linkOpts.targetDependenciesField = 'dependencies';
            }
            globalPkgNames = pkgNames.filter((pkgName) => !pkgsFoundInWorkspace.some((pkgFromWorkspace) => pkgFromWorkspace.manifest.name === pkgName));
        }
        else {
            globalPkgNames = pkgNames;
        }
        const globalPkgPath = pathAbsolute(opts.globalDir);
        globalPkgNames.forEach((pkgName) => pkgPaths.push(path.join(globalPkgPath, 'node_modules', pkgName)));
    }
    await Promise.all(pkgPaths.map((dir) => installLimit(async () => {
        const s = await createStoreController_1.cached(storeControllerCache, opts);
        await supi_1.install(await readImporterManifest_1.readImporterManifestOnly(dir, opts), {
            ...await getConfig_1.default({ ...opts.cliArgs, 'dir': dir }, {
                command: ['link'],
                excludeReporter: true,
            }),
            localPackages,
            storeController: s.ctrl,
            storeDir: s.dir,
        });
    })));
    const { manifest, writeImporterManifest } = await readImporterManifest_1.default(cwd, opts);
    // When running `pnpm link --production ../source`
    // only the `source` project should be pruned using the --production flag.
    // The target directory should keep its existing dependencies.
    // Except the ones that are replaced by the link.
    delete linkOpts.include;
    const newManifest = await supi_1.link(pkgPaths, path.join(cwd, 'node_modules'), {
        ...linkOpts,
        manifest,
    });
    await writeImporterManifest(newManifest);
    await Promise.all(Array.from(storeControllerCache.values())
        .map(async (storeControllerPromise) => {
        const storeControllerHolder = await storeControllerPromise;
        await storeControllerHolder.ctrl.close();
    }));
};
