var mout = require('mout');
var Q = require('q');
var Project = require('../core/Project');
var defaultConfig = require('../config');

function uninstall(logger, names, options, config) {
    if (!names.length) {
        return new Q();
    }

    var project;

    options = options || {};
    config = defaultConfig(config);
    project = new Project(config, logger);

    return project.getTree(options).spread(function(tree, flattened) {
        // Uninstall nodes
        return (
            project
                .uninstall(names, options)
                // Clean out non-shared uninstalled dependencies
                .then(function(uninstalled) {
                    var names = Object.keys(uninstalled);
                    var children = [];

                    // Grab the dependencies of packages that were uninstalled
                    mout.object.forOwn(flattened, function(node) {
                        if (names.indexOf(node.endpoint.name) !== -1) {
                            children.push.apply(
                                children,
                                mout.object.keys(node.dependencies)
                            );
                        }
                    });

                    // Clean them!
                    return clean(project, children, uninstalled);
                })
        );
    });
}

function clean(project, names, removed) {
    removed = removed || {};

    return project.getTree().spread(function(tree, flattened) {
        var nodes = [];
        var dependantsCounter = {};

        // Grab the nodes of each specified name
        mout.object.forOwn(flattened, function(node) {
            if (names.indexOf(node.endpoint.name) !== -1) {
                nodes.push(node);
            }
        });

        // Walk the down the tree, gathering dependants of the packages
        project.walkTree(
            tree,
            function(node, nodeName) {
                if (names.indexOf(nodeName) !== -1) {
                    dependantsCounter[nodeName] =
                        dependantsCounter[nodeName] || 0;
                    dependantsCounter[nodeName] += node.nrDependants;
                }
            },
            true
        );

        // Filter out those that have no dependants
        nodes = nodes.filter(function(node) {
            return !dependantsCounter[node.endpoint.name];
        });

        // Are we done?
        if (!nodes.length) {
            return Q.resolve(removed);
        }

        // Grab the nodes after filtering
        names = nodes.map(function(node) {
            return node.endpoint.name;
        });

        // Uninstall them
        return (
            project
                .uninstall(names)
                // Clean out non-shared uninstalled dependencies
                .then(function(uninstalled) {
                    var children;

                    mout.object.mixIn(removed, uninstalled);

                    // Grab the dependencies of packages that were uninstalled
                    children = [];
                    nodes.forEach(function(node) {
                        children.push.apply(
                            children,
                            mout.object.keys(node.dependencies)
                        );
                    });

                    // Recurse!
                    return clean(project, children, removed);
                })
        );
    });
}

// -------------------

uninstall.readOptions = function(argv) {
    var cli = require('../util/cli');

    var options = cli.readOptions(
        {
            save: { type: Boolean, shorthand: 'S' },
            'save-dev': { type: Boolean, shorthand: 'D' }
        },
        argv
    );

    var names = options.argv.remain.slice(1);

    delete options.argv;

    return [names, options];
};

module.exports = uninstall;
