"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_locker_1 = require("@pnpm/fs-locker");
const logger_1 = require("@pnpm/logger");
const package_requester_1 = require("@pnpm/package-requester");
const pFilter = require("p-filter");
const pLimit = require("p-limit");
const path = require("path");
const exists = require("path-exists");
const R = require("ramda");
const rimraf = require("rimraf-then");
const storeIndex_1 = require("../fs/storeIndex");
const createImportPackage_1 = require("./createImportPackage");
function default_1(resolve, fetchers, initOpts) {
    return __awaiter(this, void 0, void 0, function* () {
        const unlock = initOpts.locks
            ? yield fs_locker_1.default(initOpts.store, {
                locks: initOpts.locks,
                stale: initOpts.lockStaleDuration || 60 * 1000,
                whenLocked: () => logger_1.storeLogger.warn(`waiting for the store at "${initOpts.store}" to be unlocked...`),
            })
            : null;
        const store = initOpts.store;
        const storeIndex = (yield storeIndex_1.read(initOpts.store)) || {};
        const packageRequester = package_requester_1.default(resolve, fetchers, {
            networkConcurrency: initOpts.networkConcurrency,
            storeIndex,
            storePath: initOpts.store,
        });
        return {
            close: unlock ? () => __awaiter(this, void 0, void 0, function* () { yield unlock(); }) : () => Promise.resolve(undefined),
            closeSync: unlock ? () => unlock.sync() : () => undefined,
            fetchPackage: packageRequester.fetchPackageToStore,
            getCacheByEngine: package_requester_1.getCacheByEngine,
            importPackage: createImportPackage_1.default(initOpts.packageImportMethod),
            prune,
            requestPackage: packageRequester.requestPackage,
            saveState: storeIndex_1.save.bind(null, initOpts.store, storeIndex),
            saveStateSync: storeIndex_1.saveSync.bind(null, initOpts.store, storeIndex),
            updateConnections: (prefix, opts) => __awaiter(this, void 0, void 0, function* () {
                yield removeDependencies(prefix, opts.removeDependencies, { prune: opts.prune });
                yield addDependencies(prefix, opts.addDependencies);
            }),
            upload,
        };
        function removeDependencies(prefix, dependencyPkgIds, opts) {
            return __awaiter(this, void 0, void 0, function* () {
                yield Promise.all(dependencyPkgIds.map((notDependent) => __awaiter(this, void 0, void 0, function* () {
                    if (storeIndex[notDependent]) {
                        storeIndex[notDependent].splice(storeIndex[notDependent].indexOf(prefix), 1);
                        if (opts.prune && !storeIndex[notDependent].length) {
                            delete storeIndex[notDependent];
                            yield rimraf(path.join(store, notDependent));
                        }
                    }
                })));
            });
        }
        function addDependencies(prefix, dependencyPkgIds) {
            return __awaiter(this, void 0, void 0, function* () {
                dependencyPkgIds.forEach((newDependent) => {
                    storeIndex[newDependent] = storeIndex[newDependent] || [];
                    if (storeIndex[newDependent].indexOf(prefix) === -1) {
                        storeIndex[newDependent].push(prefix);
                    }
                });
            });
        }
        function prune() {
            return __awaiter(this, void 0, void 0, function* () {
                const removedProjects = yield getRemovedProject(storeIndex);
                for (const pkgId in storeIndex) {
                    if (storeIndex.hasOwnProperty(pkgId)) {
                        storeIndex[pkgId] = R.difference(storeIndex[pkgId], removedProjects);
                        if (!storeIndex[pkgId].length) {
                            delete storeIndex[pkgId];
                            yield rimraf(path.join(store, pkgId));
                            logger_1.storeLogger.info(`- ${pkgId}`);
                        }
                    }
                }
            });
        }
        function upload(builtPkgLocation, opts) {
            return __awaiter(this, void 0, void 0, function* () {
                const cachePath = path.join(store, opts.pkgId, 'side_effects', opts.engine, 'package');
                // TODO calculate integrity.json here
                const filenames = [];
                yield createImportPackage_1.copyPkg(builtPkgLocation, cachePath, { filesResponse: { fromStore: true, filenames }, force: true });
            });
        }
    });
}
exports.default = default_1;
const limitExistsCheck = pLimit(10);
function getRemovedProject(storeIndex) {
    return __awaiter(this, void 0, void 0, function* () {
        const allProjects = R.uniq(R.unnest(R.values(storeIndex)));
        return pFilter(allProjects, (projectPath) => limitExistsCheck(() => __awaiter(this, void 0, void 0, function* () {
            const modulesDir = path.join(projectPath, 'node_modules');
            return !(yield exists(modulesDir));
        })));
    });
}
//# sourceMappingURL=index.js.map