import importlib
import os
import sys

import pipenv.vendor.attr as attr
from pipenv.patched.pip._internal.utils.temp_dir import global_tempdir_manager
from pipenv.patched.pip._internal.vcs.versioncontrol import VcsSupport

from ..environment import MYPY_RUNNING
from .url import URI

if MYPY_RUNNING:
    from typing import Any, Optional, Tuple


@attr.s(hash=True)
class VCSRepository(object):
    DEFAULT_RUN_ARGS = None

    url = attr.ib()  # type: str
    name = attr.ib()  # type: str
    checkout_directory = attr.ib()  # type: str
    vcs_type = attr.ib()  # type: str
    parsed_url = attr.ib()  # type: URI
    subdirectory = attr.ib(default=None)  # type: Optional[str]
    commit_sha = attr.ib(default=None)  # type: Optional[str]
    ref = attr.ib(default=None)  # type: Optional[str]
    repo_backend = attr.ib()  # type: Any
    clone_log = attr.ib(default=None)  # type: Optional[str]

    @parsed_url.default
    def get_parsed_url(self):
        # type: () -> URI
        return URI.parse(self.url)

    @repo_backend.default
    def get_repo_backend(self):
        if self.DEFAULT_RUN_ARGS is None:
            default_run_args = self.monkeypatch_pip()
        else:
            default_run_args = self.DEFAULT_RUN_ARGS

        VCS_SUPPORT = VcsSupport()
        backend = VCS_SUPPORT.get_backend(self.vcs_type)
        # repo = backend(url=self.url)
        if backend.run_command.__func__.__defaults__ != default_run_args:
            backend.run_command.__func__.__defaults__ = default_run_args
        return backend

    @property
    def is_local(self):
        # type: () -> bool
        url = self.url
        if "+" in url:
            url = url.split("+")[1]
        return url.startswith("file")

    def obtain(self, verbosity=1) -> None:
        if os.path.exists(
            self.checkout_directory
        ) and not self.repo_backend.is_repository_directory(self.checkout_directory):
            self.repo_backend.unpack(self.checkout_directory)
        elif not os.path.exists(self.checkout_directory):
            self.repo_backend.obtain(self.checkout_directory, self.parsed_url, verbosity)
        else:
            if self.ref:
                self.checkout_ref(self.ref)
        if not self.commit_sha:
            self.commit_sha = self.get_commit_hash()

    def checkout_ref(self, ref):
        # type: (str) -> None
        rev_opts = self.repo_backend.make_rev_options(ref)
        if not any(
            [
                self.repo_backend.is_commit_id_equal(self.checkout_directory, ref),
                self.repo_backend.is_commit_id_equal(self.checkout_directory, rev_opts),
                self.is_local,
            ]
        ):
            self.update(ref)

    def update(self, ref):
        # type: (str) -> None
        target_ref = self.repo_backend.make_rev_options(ref)
        self.repo_backend.update(self.checkout_directory, self.url, target_ref)
        self.commit_sha = self.get_commit_hash()

    def get_commit_hash(self, ref=None):
        # type: (Optional[str]) -> str
        with global_tempdir_manager():
            return self.repo_backend.get_revision(self.checkout_directory)

    @classmethod
    def monkeypatch_pip(cls):
        # type: () -> Tuple[Any, ...]
        target_module = VcsSupport.__module__
        pip_vcs = importlib.import_module(target_module)
        run_command_defaults = pip_vcs.VersionControl.run_command.__func__.__defaults__
        # set the default to not write stdout, the first option sets this value
        new_defaults = [False] + list(run_command_defaults)[1:]
        new_defaults = tuple(new_defaults)
        pip_vcs.VersionControl.run_command.__func__.__defaults__ = new_defaults
        sys.modules[target_module] = pip_vcs
        cls.DEFAULT_RUN_ARGS = new_defaults
        return new_defaults
