# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
"""CLI implementation for `conda remove`.

Removes the specified packages from an existing environment.
"""

import logging
from argparse import ArgumentParser, Namespace, _SubParsersAction
from os.path import isfile, join

from .common import confirm_yn

log = logging.getLogger(__name__)


def configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser:
    from ..auxlib.ish import dals
    from ..common.constants import NULL
    from .actions import NullCountAction
    from .helpers import (
        add_output_and_prompt_options,
        add_parser_channels,
        add_parser_networking,
        add_parser_prefix,
        add_parser_prune,
        add_parser_pscheck,
        add_parser_solver,
    )

    summary = "Remove a list of packages from a specified conda environment. "
    description = dals(
        f"""
        {summary}

        Use `--all` flag to remove all packages and the environment itself.

        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.
        """
    )
    epilog = dals(
        """
        Examples:

        Remove the package 'scipy' from the currently-active environment::

            conda remove scipy

        Remove a list of packages from an environment 'myenv'::

            conda remove -n myenv scipy curl wheel

        Remove all packages from environment `myenv` and the environment itself::

            conda remove -n myenv --all

        Remove all packages from the environment `myenv` but retain the environment::

            conda remove -n myenv --all --keep-env

        """
    )

    p = sub_parsers.add_parser(
        "remove",
        help=summary,
        description=description,
        epilog=epilog,
        **kwargs,
    )
    add_parser_pscheck(p)

    add_parser_prefix(p)
    add_parser_channels(p)

    solver_mode_options = p.add_argument_group("Solver Mode Modifiers")
    solver_mode_options.add_argument(
        "--features",
        action="store_true",
        help="Remove features (instead of packages).",
    )
    solver_mode_options.add_argument(
        "--force-remove",
        "--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.",
        dest="force_remove",
    )
    solver_mode_options.add_argument(
        "--no-pin",
        action="store_true",
        dest="ignore_pinned",
        default=NULL,
        help="Ignore pinned package(s) that apply to the current operation. "
        "These pinned packages might come from a .condarc file or a file in "
        "<TARGET_ENVIRONMENT>/conda-meta/pinned.",
    )
    add_parser_prune(solver_mode_options)
    add_parser_solver(solver_mode_options)

    add_parser_networking(p)
    add_output_and_prompt_options(p)

    p.add_argument(
        "--all",
        action="store_true",
        help="Remove all packages, i.e., the entire environment.",
    )
    p.add_argument(
        "--keep-env",
        action="store_true",
        help="Used with `--all`, delete all packages but keep the environment.",
    )
    p.add_argument(
        "package_names",
        metavar="package_name",
        action="store",
        nargs="*",
        help="Package names to remove from the environment.",
    )
    p.add_argument(
        "--dev",
        action=NullCountAction,
        help="Use `sys.executable -m conda` in wrapper scripts instead of CONDA_EXE. "
        "This is mainly for use during tests where we test new conda sources "
        "against old Python versions.",
        dest="dev",
        default=NULL,
    )

    p.set_defaults(func="conda.cli.main_remove.execute")

    return p


def execute(args: Namespace, parser: ArgumentParser) -> int:
    from ..base.context import context
    from ..core.envs_manager import unregister_env
    from ..core.link import PrefixSetup, UnlinkLinkTransaction
    from ..core.prefix_data import PrefixData
    from ..exceptions import (
        CondaEnvironmentError,
        CondaValueError,
        DirectoryNotACondaEnvironmentError,
        PackagesNotFoundError,
    )
    from ..gateways.disk.delete import path_is_clean, rm_rf
    from ..models.match_spec import MatchSpec
    from .common import check_non_admin, specs_from_args
    from .install import handle_txn

    if not (args.all or args.package_names):
        raise CondaValueError(
            "no package names supplied,\n"
            '       try "conda remove -h" for more details'
        )

    prefix = context.target_prefix
    check_non_admin()

    if args.all and prefix == context.default_prefix:
        msg = "cannot remove current environment. deactivate and run conda remove again"
        raise CondaEnvironmentError(msg)

    if args.all and path_is_clean(prefix):
        # full environment removal was requested, but environment doesn't exist anyway

        # .. but you know what? If you call `conda remove --all` you'd expect the dir
        # not to exist afterwards, would you not? If not (fine, I can see the argument
        # about deleting people's work in envs being a very bad thing indeed), but if
        # being careful is the goal it would still be nice if after `conda remove --all`
        # to be able to do `conda create` on the same environment name.
        #
        # try:
        #     rm_rf(prefix, clean_empty_parents=True)
        # except:
        #     log.warning("Failed rm_rf() of partially existent env {}".format(prefix))

        return 0

    if args.all:
        if prefix == context.root_prefix:
            raise CondaEnvironmentError(
                "cannot remove root environment, add -n NAME or -p PREFIX option"
            )
        if not isfile(join(prefix, "conda-meta", "history")):
            raise DirectoryNotACondaEnvironmentError(prefix)
        if not args.json:
            print(f"\nRemove all packages in environment {prefix}:\n")

        if "package_names" in args:
            stp = PrefixSetup(
                target_prefix=prefix,
                unlink_precs=tuple(PrefixData(prefix).iter_records()),
                link_precs=(),
                remove_specs=(),
                update_specs=(),
                neutered_specs={},
            )
            txn = UnlinkLinkTransaction(stp)
            try:
                handle_txn(txn, prefix, args, False, True)
            except PackagesNotFoundError:
                if not args.json:
                    print(
                        f"No packages found in {prefix}. Continuing environment removal"
                    )
        if not context.dry_run:
            if not args.keep_env:
                if not args.json:
                    confirm_yn(
                        f"Everything found within the environment ({prefix}), including any conda environment configurations and any non-conda files, will be deleted. Do you wish to continue?\n",
                        default="no",
                        dry_run=False,
                    )
                rm_rf(prefix, clean_empty_parents=True)
                unregister_env(prefix)

        return 0

    else:
        if args.features:
            specs = tuple(MatchSpec(track_features=f) for f in set(args.package_names))
        else:
            specs = specs_from_args(args.package_names)
        channel_urls = ()
        subdirs = ()
        solver_backend = context.plugin_manager.get_cached_solver_backend()
        solver = solver_backend(prefix, channel_urls, subdirs, specs_to_remove=specs)
        txn = solver.solve_for_transaction()
        handle_txn(txn, prefix, args, False, True)
        return 0
