# (c) 2012-2013 Continuum Analytics, Inc. / http://continuum.io
# All Rights Reserved
#
# conda is distributed under the terms of the BSD 3-clause license.
# Consult LICENSE.txt or http://opensource.org/licenses/BSD-3-Clause.

from __future__ import print_function, division, absolute_import

import errno
import logging
from argparse import RawDescriptionHelpFormatter
from os.path import join

from .common import (add_parser_help, add_parser_yes, add_parser_json, add_parser_no_pin,
                     add_parser_channels, add_parser_prefix, add_parser_quiet,
                     add_parser_no_use_index_cache, add_parser_use_index_cache,
                     add_parser_use_local, add_parser_offline, add_parser_pscheck,
                     InstalledPackages, error_and_exit, get_prefix, check_write,
                     ensure_use_local, ensure_override_channels_requires_channel,
                     get_index_trap, specs_from_args, names_in_specs, root_no_rm, stdout_json,
                     confirm_yn)
from ..config import default_prefix
from ..console import json_progress_bars
from ..compat import iteritems, iterkeys


help = "%s a list of packages from a specified conda environment."
descr = help + """

This command will also remove any package that depends on any of the
specified packages as well---unless a replacement can be found without
that dependency. If you wish to skip this dependency checking and remove
just the requested packages, add the '--force' option. Note however that
this may result in a broken environment, so use this with caution.
"""
example = """
Examples:

    conda %s -n myenv scipy

"""

uninstall_help = "Alias for conda remove.  See conda remove --help."
log = logging.getLogger(__name__)

def configure_parser(sub_parsers, name='remove'):
    if name == 'remove':
        p = sub_parsers.add_parser(
            name,
            formatter_class=RawDescriptionHelpFormatter,
            description=descr % name.capitalize(),
            help=help % name.capitalize(),
            epilog=example % name,
            add_help=False,
        )
    else:
        p = sub_parsers.add_parser(
            name,
            formatter_class=RawDescriptionHelpFormatter,
            description=uninstall_help,
            help=uninstall_help,
            epilog=example % name,
            add_help=False,
        )
    add_parser_help(p)
    add_parser_yes(p)
    add_parser_json(p)
    p.add_argument(
        "--all",
        action="store_true",
        help="%s all packages, i.e., the entire environment." % name.capitalize(),
    )
    p.add_argument(
        "--features",
        action="store_true",
        help="%s features (instead of packages)." % name.capitalize(),
    )
    p.add_argument(
        "--force",
        action="store_true",
        help="Forces removal of a package without removing packages that depend on it. "
             "Using this option will usually leave your environment in a broken and "
             "inconsistent state.",
    )
    add_parser_no_pin(p)
    add_parser_channels(p)
    add_parser_prefix(p)
    add_parser_quiet(p)
    # Putting this one first makes it the default
    add_parser_no_use_index_cache(p)
    add_parser_use_index_cache(p)
    add_parser_use_local(p)
    add_parser_offline(p)
    add_parser_pscheck(p)
    p.add_argument(
        'package_names',
        metavar='package_name',
        action="store",
        nargs='*',
        help="Package names to %s from the environment." % name,
    ).completer = InstalledPackages
    p.set_defaults(func=execute)


def execute(args, parser):
    import conda.plan as plan
    import conda.instructions as inst
    from conda.install import rm_rf, linked_data

    if not (args.all or args.package_names):
        error_and_exit('no package names supplied,\n'
                       '       try "conda remove -h" for more details',
                       json=args.json,
                       error_type="ValueError")

    prefix = get_prefix(args)
    if args.all and prefix == default_prefix:
        msg = "cannot remove current environment. deactivate and run conda remove again"
        error_and_exit(msg)
    check_write('remove', prefix, json=args.json)
    ensure_use_local(args)
    ensure_override_channels_requires_channel(args)
    channel_urls = args.channel or ()
    if not args.features and args.all:
        index = linked_data(prefix)
        index = {dist + '.tar.bz2': info for dist, info in iteritems(index)}
    else:
        index = get_index_trap(channel_urls=channel_urls,
                               prepend=not args.override_channels,
                               use_local=args.use_local,
                               use_cache=args.use_index_cache,
                               json=args.json,
                               offline=args.offline,
                               prefix=prefix)
    specs = None
    if args.features:
        features = set(args.package_names)
        actions = plan.remove_features_actions(prefix, index, features)

    elif args.all:
        if plan.is_root_prefix(prefix):
            error_and_exit('cannot remove root environment,\n'
                           '       add -n NAME or -p PREFIX option',
                           json=args.json,
                           error_type="CantRemoveRoot")

        actions = {inst.PREFIX: prefix}
        for fkey in sorted(iterkeys(index)):
            plan.add_unlink(actions, fkey[:-8])

    else:
        specs = specs_from_args(args.package_names)
        if (plan.is_root_prefix(prefix) and names_in_specs(root_no_rm, specs)):
            error_and_exit('cannot remove %s from root environment' %
                           ', '.join(root_no_rm),
                           json=args.json,
                           error_type="CantRemoveFromRoot")
        actions = plan.remove_actions(prefix, specs, index=index,
                                      force=args.force, pinned=args.pinned)

    if plan.nothing_to_do(actions):
        if args.all:
            rm_rf(prefix)

            if args.json:
                stdout_json({
                    'success': True,
                    'actions': actions
                })
            return
        error_and_exit('no packages found to remove from '
                       'environment: %s' % prefix,
                       json=args.json,
                       error_type="PackageNotInstalled")

    if not args.json:
        print()
        print("Package plan for package removal in environment %s:" % prefix)
        plan.display_actions(actions, index)

    if args.json and args.dry_run:
        stdout_json({
            'success': True,
            'dry_run': True,
            'actions': actions
        })
        return

    if not args.json:
        confirm_yn(args)

    if args.json and not args.quiet:
        with json_progress_bars():
            plan.execute_actions(actions, index, verbose=not args.quiet)
    else:
        plan.execute_actions(actions, index, verbose=not args.quiet)
        if specs:
            try:
                with open(join(prefix, 'conda-meta', 'history'), 'a') as f:
                    f.write('# remove specs: %s\n' % specs)
            except IOError as e:
                if e.errno == errno.EACCES:
                    log.debug("Can't write the history file")
                else:
                    raise

    if args.all:
        rm_rf(prefix)

    if args.json:
        stdout_json({
            'success': True,
            'actions': actions
        })
