"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const find_workspace_packages_1 = require("@pnpm/find-workspace-packages");
const matcher_1 = require("@pnpm/matcher");
const isSubdir = require("is-subdir");
const pkgs_graph_1 = require("pkgs-graph");
const R = require("ramda");
const getChangedPackages_1 = require("./getChangedPackages");
const parsePackageSelector_1 = require("./parsePackageSelector");
exports.parsePackageSelector = parsePackageSelector_1.default;
async function readProjects(workspaceDir, pkgSelectors) {
    const allProjects = await find_workspace_packages_1.default(workspaceDir, {});
    const selectedProjectsGraph = await filterPkgsBySelectorObjects(allProjects, pkgSelectors, {
        workspaceDir,
    });
    return { allProjects, selectedProjectsGraph };
}
exports.readProjects = readProjects;
async function filterPackages(pkgs, filter, opts) {
    const packageSelectors = filter.
        map((f) => parsePackageSelector_1.default(f, opts.prefix));
    return filterPkgsBySelectorObjects(pkgs, packageSelectors, opts);
}
exports.filterPackages = filterPackages;
async function filterPkgsBySelectorObjects(pkgs, packageSelectors, opts) {
    const { graph } = pkgs_graph_1.default(pkgs);
    if (packageSelectors && packageSelectors.length) {
        return filterGraph(graph, packageSelectors, {
            workspaceDir: opts.workspaceDir,
        });
    }
    else {
        return graph;
    }
}
exports.filterPkgsBySelectorObjects = filterPkgsBySelectorObjects;
async function filterGraph(pkgGraph, packageSelectors, opts) {
    const cherryPickedPackages = [];
    const walkedDependencies = new Set();
    const walkedDependents = new Set();
    const graph = pkgGraphToGraph(pkgGraph);
    let reversedGraph;
    for (const selector of packageSelectors) {
        let entryPackages;
        if (selector.namePattern) {
            entryPackages = matchPackages(pkgGraph, selector.namePattern);
        }
        else if (selector.parentDir) {
            entryPackages = matchPackagesByPath(pkgGraph, selector.parentDir);
        }
        else if (selector.diff) {
            entryPackages = await getChangedPackages_1.default(Object.keys(pkgGraph), selector.diff, { workspaceDir: opts.workspaceDir });
        }
        else {
            throw new Error(`Unsupported package selector: ${JSON.stringify(selector)}`);
        }
        if (selector.includeDependencies) {
            pickSubgraph(graph, entryPackages, walkedDependencies, { includeRoot: !selector.excludeSelf });
        }
        if (selector.includeDependents) {
            if (!reversedGraph) {
                reversedGraph = reverseGraph(graph);
            }
            pickSubgraph(reversedGraph, entryPackages, walkedDependents, { includeRoot: !selector.excludeSelf });
        }
        if (!selector.includeDependencies && !selector.includeDependents) {
            Array.prototype.push.apply(cherryPickedPackages, entryPackages);
        }
    }
    const walked = new Set([...walkedDependencies, ...walkedDependents]);
    cherryPickedPackages.forEach((cherryPickedPackage) => walked.add(cherryPickedPackage));
    return R.pick(Array.from(walked), pkgGraph);
}
exports.default = filterGraph;
function pkgGraphToGraph(pkgGraph) {
    const graph = {};
    Object.keys(pkgGraph).forEach((nodeId) => {
        graph[nodeId] = pkgGraph[nodeId].dependencies;
    });
    return graph;
}
function reverseGraph(graph) {
    const reversedGraph = {};
    Object.keys(graph).forEach((dependentNodeId) => {
        graph[dependentNodeId].forEach((dependencyNodeId) => {
            if (!reversedGraph[dependencyNodeId]) {
                reversedGraph[dependencyNodeId] = [dependentNodeId];
            }
            else {
                reversedGraph[dependencyNodeId].push(dependentNodeId);
            }
        });
    });
    return reversedGraph;
}
function matchPackages(graph, pattern) {
    const match = matcher_1.default(pattern);
    return Object.keys(graph).filter((id) => graph[id].package.manifest.name && match(graph[id].package.manifest.name));
}
function matchPackagesByPath(graph, pathStartsWith) {
    return Object.keys(graph).filter((parentDir) => isSubdir(pathStartsWith, parentDir));
}
function pickSubgraph(graph, nextNodeIds, walked, opts) {
    for (const nextNodeId of nextNodeIds) {
        if (!walked.has(nextNodeId)) {
            if (opts.includeRoot) {
                walked.add(nextNodeId);
            }
            if (graph[nextNodeId])
                pickSubgraph(graph, graph[nextNodeId], walked, { includeRoot: true });
        }
    }
}
