# -*- coding=utf-8 -*-
from __future__ import absolute_import

import argparse
import os
import sys

import tomlkit.exceptions

import passa.models.projects
import vistir


PYTHON_VERSION = ".".join(str(v) for v in sys.version_info[:2])


class Project(passa.models.projects.Project):
    def __init__(self, root, *args, **kwargs):
        root = vistir.compat.Path(root).absolute()
        pipfile = root.joinpath("Pipfile")
        if not pipfile.is_file():
            raise argparse.ArgumentError(
                "{0!r} is not a Pipfile project".format(root),
            )
        try:
            super(Project, self).__init__(root.as_posix(), *args, **kwargs)
        except tomlkit.exceptions.ParseError as e:
            raise argparse.ArgumentError(
                "failed to parse Pipfile: {0!r}".format(str(e)),
            )

    def __name__(self):
        return "Project Root"


class Option(object):
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def add_to_parser(self, parser):
        parser.add_argument(*self.args, **self.kwargs)

    def add_to_group(self, group):
        group.add_argument(*self.args, **self.kwargs)


class ArgumentGroup(object):
    def __init__(
            self, name, parser=None,
            is_mutually_exclusive=False,
            required=None, options=None):
        self.name = name
        self.options = options or []
        self.parser = parser
        self.required = required
        self.is_mutually_exclusive = is_mutually_exclusive
        self.argument_group = None

    def add_to_parser(self, parser):
        group = None
        if self.is_mutually_exclusive:
            group = parser.add_mutually_exclusive_group(required=self.required)
        else:
            group = parser.add_argument_group()
        for option in self.options:
            option.add_to_group(group)
        self.argument_group = group
        self.parser = parser


project = Option(
    "--project", metavar="project", default=os.getcwd(), type=Project,
    help="path to project root (directory containing Pipfile)",
)

new_project = Option(
    "--project", metavar="project", default=os.getcwd(), type=str,
    help="path to project root (directory containing Pipfile)",
)

python_version = Option(
    "--py-version", "--python-version", "--requires-python", metavar="python-version",
    dest="python_version", default=PYTHON_VERSION, type=str,
    help="required minor python version for the project"
)

packages = Option(
    "packages", metavar="package", nargs="*",
    help="requirement to add (can be used multiple times)",
)

editable = Option(
    '-e', '--editable', dest='editables', nargs="*", default=[], metavar='path/vcs',
    help="editable requirement to add (can be used multiple times)",
)

dev = Option(
    "--dev", action="store_true", default=False,
    help="Use [dev-packages] for install/freeze/uninstall operations",
)

no_sync = Option(
    "--no-sync", dest="sync", action="store_false", default=True,
    help="do not synchronize the environment",
)

target = Option(
    "-t", "--target", default=None,
    help="file to export into (default is to print to stdout)"
)

no_default = Option(
    "--no-default", dest="default", action="store_false", default=True,
    help="do not include default packages when exporting, importing, or cleaning"
)

include_hashes = Option(
    "--include-hashes", dest="include_hashes", action="store_true",
    help="output hashes in requirements.txt (default is to guess)",
)

no_include_hashes = Option(
    "--no-include-hashes", dest="include_hashes", action="store_false",
    help="do not output hashes in requirements.txt (default is to guess)",
)

no_check = Option(
    "--no-check", dest="check", action="store_false", default=True,
    help="do not check if Pipfile.lock is up to date, always resolve",
)

no_clean = Option(
    "--no-clean", dest="clean", action="store_false", default=True,
    help="do not remove packages not specified in Pipfile.lock",
)

dev_only = Option(
    "--dev", dest="only", action="store_const", const="dev",
    help="only try to modify [dev-packages]",
)

default_only = Option(
    "--default", dest="only", action="store_const", const="default",
    help="only try to modify [default]",
)

strategy = Option(
    "--strategy", choices=["eager", "only-if-needed"], default="only-if-needed",
    help="how dependency upgrading is handled",
)

include_hashes_group = ArgumentGroup("include_hashes", is_mutually_exclusive=True, options=[include_hashes, no_include_hashes])
dev_group = ArgumentGroup("dev", is_mutually_exclusive="True", options=[dev_only, default_only])
package_group = ArgumentGroup("packages", options=[packages, editable, dev, no_sync])
new_project_group = ArgumentGroup("new-project", options=[new_project, python_version])
