import argparse
import sys
import traceback

from . import __version__
from .core import CondaPackException, context, pack


class MultiAppendAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, **kwargs):
        if nargs is not None:
            raise ValueError("nargs not allowed")
        super().__init__(option_strings, dest, **kwargs)

    def __call__(self, parser, namespace, values, option_string=None):
        if getattr(namespace, self.dest) is None:
            setattr(namespace, self.dest, [])
        getattr(namespace, self.dest).append((option_string.strip('-'), values))


def build_parser():
    description = "Package an existing conda environment into an archive file."
    kwargs = dict(prog="conda-pack", description=description, add_help=False)
    kwargs["allow_abbrev"] = False
    parser = argparse.ArgumentParser(**kwargs)
    parser.add_argument("--name", "-n",
                        metavar="ENV",
                        help="The name of the environment to pack. "
                        "If neither --name nor --prefix are supplied, "
                        "the current activated environment is packed.")
    parser.add_argument("--prefix", "-p",
                        metavar="PATH",
                        help="The path to the environment to pack. "
                        "Only one of --name/--prefix should be supplied.")
    parser.add_argument("--output", "-o",
                        metavar="PATH",
                        help=("The path of the output file. Defaults to the "
                              "environment name with a ``.tar.gz`` suffix "
                              "(e.g.  ``my_env.tar.gz``)."))
    parser.add_argument("--arcroot",
                        metavar="PATH", default='',
                        help=("The relative path in the archive to the conda "
                              "environment. Defaults to ''."))
    parser.add_argument("--dest-prefix", "-d",
                        metavar="PATH",
                        help=("If present, prefixes will be rewritten to this "
                              "path before packaging. In this case the "
                              "`conda-unpack` script will not be generated. "
                              "This option should not be used with parcels, which "
                              "instead generate their destination prefix from the "
                              "--parcel-root, --parcel-name, and "
                              "--parcel-version options."))
    parser.add_argument("--parcel-root", default=None,
                        help="(Parcels only) The location where all parcels are unpacked "
                        "on the target Hadoop cluster (default: '/opt/cloudera/parcels').")
    parser.add_argument("--parcel-name", default=None,
                        help="(Parcels only) The name of the parcel, without a version "
                        "suffix. The default value is the local environment name. The parcel "
                        "name may not have any hyphens.")
    parser.add_argument("--parcel-version", default=None,
                        help="(Parcels only) The version number for the parcel. The default "
                        "value is the current date, using the format YYYY.MM.DD.")
    parser.add_argument("--parcel-distro", default=None,
                        help="(Parcels only) The distribution type for the parcel. The "
                        "default value is 'el7'. This value cannot have any hyphens.")
    parser.add_argument("--format",
                        choices=['infer', 'zip', 'tar.gz', 'tgz', 'tar.bz2',
                                 'tbz2', 'tar.xz', 'txz', 'tar', 'parcel', 'squashfs'],
                        default='infer',
                        help=("The archival format to use. By default this is "
                              "inferred by the output file extension."))
    parser.add_argument("--compress-level",
                        metavar="LEVEL",
                        type=int,
                        default=4,
                        help=("The compression level to use, from 0 to 9. "
                              "Higher numbers decrease output file size at "
                              "the expense of compression time. Default is 4."))
    parser.add_argument("--n-threads", "-j",
                        metavar="N",
                        type=int,
                        default=1,
                        help=("The number of threads to use. Set to -1 to use "
                              "the number of cpus on this machine. If a file "
                              "format doesn't support threaded packaging, this "
                              "option will be ignored. Default is 1."))
    parser.add_argument("--zip-symlinks",
                        action="store_true",
                        help=("Symbolic links aren't supported by the Zip "
                              "standard, but are supported by *many* common "
                              "Zip implementations. If set, store symbolic "
                              "links in the archive, instead of the file "
                              "referred to by the link. This can avoid storing "
                              "multiple copies of the same files. *Note that "
                              "the resulting archive may silently fail on "
                              "decompression if the ``unzip`` implementation "
                              "doesn't support symlinks*. Ignored if format "
                              "isn't ``zip``."))
    parser.add_argument("--no-zip-64",
                        action="store_true",
                        help="Disable ZIP64 extensions.")
    parser.add_argument("--ignore-editable-packages",
                        action="store_true",
                        help="Skips checks for editable packages.")
    parser.add_argument("--ignore-missing-files",
                        action="store_true",
                        help="Skip checks for missing package files.")
    parser.add_argument("--exclude",
                        action=MultiAppendAction,
                        metavar="PATTERN",
                        dest="filters",
                        help="Exclude files matching this pattern")
    parser.add_argument("--include",
                        action=MultiAppendAction,
                        metavar="PATTERN",
                        dest="filters",
                        help="Re-add excluded files matching this pattern")
    parser.add_argument("--force", "-f",
                        action="store_true",
                        help="Overwrite any existing archive at the output path.")
    parser.add_argument("--quiet", "-q",
                        action="store_true",
                        help="Do not report progress")
    parser.add_argument("--help", "-h", action='help',
                        help="Show this help message then exit")
    parser.add_argument("--version",
                        action='store_true',
                        help="Show version then exit")
    return parser


# Parser at top level to allow sphinxcontrib.autoprogram to work
PARSER = build_parser()


def fail(msg):
    print(msg, file=sys.stderr)
    sys.exit(1)


def main(args=None, pack=pack):
    args = PARSER.parse_args(args=args)

    # Manually handle version printing to output to stdout in python < 3.4
    if args.version:
        print('conda-pack %s' % __version__)
        sys.exit(0)

    try:
        with context.set_cli():
            pack(name=args.name,
                 prefix=args.prefix,
                 output=args.output,
                 format=args.format,
                 force=args.force,
                 compress_level=args.compress_level,
                 n_threads=args.n_threads,
                 zip_symlinks=args.zip_symlinks,
                 zip_64=not args.no_zip_64,
                 arcroot=args.arcroot,
                 dest_prefix=args.dest_prefix,
                 parcel_root=args.parcel_root,
                 parcel_name=args.parcel_name,
                 parcel_version=args.parcel_version,
                 parcel_distro=args.parcel_distro,
                 verbose=not args.quiet,
                 filters=args.filters,
                 ignore_editable_packages=args.ignore_editable_packages,
                 ignore_missing_files=args.ignore_missing_files)
    except CondaPackException as e:
        fail("CondaPackError: %s" % e)
    except KeyboardInterrupt:
        fail("Interrupted")
    except Exception:
        fail(traceback.format_exc())
    sys.exit(0)


if __name__ == '__main__':
    main()
