import os
import os.path
import shlex
import subprocess
import sys

from . import _utils, _pythoninfo


GET_PIP_URL = 'https://bootstrap.pypa.io/get-pip.py'
# pip 6 is the first version supporting environment markers
MIN_PIP = '6.0'
OLD_PIP = '7.1.2'
OLD_SETUPTOOLS = '18.5'


def get_pkg_name(req):
    """Return the name of the package in the given requirement text."""
    # strip env markers
    req = req.partition(';')[0]
    # strip version
    req = req.partition('==')[0]
    req = req.partition('>=')[0]
    return req


def get_best_pip_version(python):
    """Return the pip to install for the given Python executable."""
    if not python or isinstance(python, str):
        info = _pythoninfo.get_info(python)
    else:
        info = python
    # On Python: 3.5a0 <= version < 3.5.0 (final), install pip 7.1.2,
    # the last version working on Python 3.5a0:
    # https://sourceforge.net/p/pyparsing/bugs/100/
    if 0x30500a0 <= info.sys.hexversion < 0x30500f0:
        return OLD_PIP
    else:
        return None


def run_pip(cmd, *args, **kwargs):
    """Return the result of running pip with the given args."""
    return _utils.run_python('-m', 'pip', cmd, *args, **kwargs)


def is_pip_installed(python, *, env=None):
    """Return True if pip is installed on the given Python executable."""
    ec, _, _ = run_pip(
        '--version',
        python=python,
        env=env,
        capture=True,
        verbose=False,
    )
    return ec == 0


def install_pip(python=sys.executable, *,
                info=None,
                downloaddir=None,
                env=None,
                upgrade=True,
                **kwargs
                ):
    """Install pip on the given Python executable."""
    if not python:
        python = getattr(info, 'executable', None) or sys.executable

    # python -m ensurepip
    args = ['-m', 'ensurepip', '-v']  # --verbose
    if upgrade:
        args.append('-U')  # --upgrade
    res = _utils.run_python(*args, python=python, **kwargs)
    ec, _, _ = res
    if ec == 0 and is_pip_installed(python, env=env):
        return res

    ##############################
    # Fall back to get-pip.py.

    if not downloaddir:
        downloaddir = '.'
    os.makedirs(downloaddir, exist_ok=True)

    # download get-pip.py
    filename = os.path.join(downloaddir, 'get-pip.py')
    if not os.path.exists(filename):
        print("Download %s into %s" % (GET_PIP_URL, filename))
        _utils.download(GET_PIP_URL, filename)

    # python get-pip.py
    argv = [python, '-u', filename]
    version = get_best_pip_version(info or python)
    if version:
        argv.append(version)
    res = _utils.run_cmd(argv, env=env)
    ec, _, _ = res
    if ec != 0:
        # get-pip.py was maybe not properly downloaded: remove it to
        # download it again next time
        os.unlink(filename)
    return res


def upgrade_pip(python=sys.executable, *,
                info=None,
                installer=False,
                **kwargs,
                ):
    """Upgrade pip on the given Python to the latest version."""
    if not python:
        python = getattr(info, 'executable', None) or sys.executable

    version = get_best_pip_version(info or python)
    if version:
        reqs = [f'pip=={version}']
        if installer:
            reqs.append(f'setuptools=={OLD_SETUPTOOLS}')
    else:
        # pip 6 is the first version supporting environment markers
        reqs = [f'pip>={MIN_PIP}']
    res = install_requirements(*reqs, python=python, upgrade=True, **kwargs)
    ec, _, _ = res
    if ec != 0:
        return res

    if installer:
        # Upgrade installer dependencies (setuptools, ...)
        res = ensure_installer(python, upgrade=True, **kwargs)
    return res


def ensure_installer(python=sys.executable, **kwargs):
    reqs = [
        f'setuptools>={OLD_SETUPTOOLS}',
        # install wheel so pip can cache binary wheel packages locally,
        # and install prebuilt wheel packages from PyPI.
        'wheel',
    ]
    return install_requirements(*reqs, python=python, **kwargs)


def install_requirements(reqs, *extra,
                         upgrade=True,
                         **kwargs
                         ):
    """Install the given packages from PyPI."""
    args = []
    if upgrade:
        args.append('-U')  # --upgrade
    for reqs in [reqs, *extra]:
        if os.path.isfile(reqs) and reqs.endswith('.txt'):
            args.append('-r')  # --requirement
        args.append(reqs)
    return run_pip('install', *args, **kwargs)


def install_editable(projectroot, **kwargs):
    """Install the given project as an "editable" install."""
    return run_pip('install', '-e', projectroot, **kwargs)
