import os
import ssl
import tempfile
import textwrap

import pytest

from tests.lib.server import (
    authorization_response,
    file_response,
    make_mock_server,
    package_page,
    server_running,
)


def test_options_from_env_vars(script):
    """
    Test if ConfigOptionParser reads env vars (e.g. not using PyPI here)

    """
    script.environ['PIP_NO_INDEX'] = '1'
    result = script.pip('install', '-vvv', 'INITools', expect_error=True)
    assert "Ignoring indexes:" in result.stdout, str(result)
    msg = "DistributionNotFound: No matching distribution found for INITools"
    # Case insensitive as the new resolver canonicalises the project name
    assert msg.lower() in result.stdout.lower(), str(result)


def test_command_line_options_override_env_vars(script, virtualenv):
    """
    Test that command line options override environmental variables.

    """
    script.environ['PIP_INDEX_URL'] = 'https://example.com/simple/'
    result = script.pip('install', '-vvv', 'INITools', expect_error=True)
    assert (
        "Getting page https://example.com/simple/initools"
        in result.stdout
    )
    virtualenv.clear()
    result = script.pip(
        'install', '-vvv', '--index-url', 'https://download.zope.org/ppix',
        'INITools',
        expect_error=True,
    )
    assert "example.com" not in result.stdout
    assert "Getting page https://download.zope.org/ppix" in result.stdout


@pytest.mark.network
def test_env_vars_override_config_file(script, virtualenv):
    """
    Test that environmental variables override settings in config files.
    """
    config_file = script.scratch_path / "test-pip.cfg"
    # set this to make pip load it
    script.environ['PIP_CONFIG_FILE'] = str(config_file)
    # It's important that we test this particular config value ('no-index')
    # because there is/was a bug which only shows up in cases in which
    # 'config-item' and 'config_item' hash to the same value modulo the size
    # of the config dictionary.
    config_file.write_text(textwrap.dedent("""\
        [global]
        no-index = 1
        """))
    result = script.pip('install', '-vvv', 'INITools', expect_error=True)
    msg = "DistributionNotFound: No matching distribution found for INITools"
    # Case insensitive as the new resolver canonicalises the project name
    assert msg.lower() in result.stdout.lower(), str(result)
    script.environ['PIP_NO_INDEX'] = '0'
    virtualenv.clear()
    result = script.pip('install', '-vvv', 'INITools')
    assert "Successfully installed INITools" in result.stdout


@pytest.mark.network
def test_command_line_append_flags(script, virtualenv, data):
    """
    Test command line flags that append to defaults set by environmental
    variables.

    """
    script.environ['PIP_FIND_LINKS'] = 'https://test.pypi.org'
    result = script.pip(
        'install', '-vvv', 'INITools', '--trusted-host',
        'test.pypi.org',
    )
    assert (
        "Fetching project page and analyzing links: https://test.pypi.org"
        in result.stdout
    ), str(result)
    virtualenv.clear()
    result = script.pip(
        'install', '-vvv', '--find-links', data.find_links, 'INITools',
        '--trusted-host', 'test.pypi.org',
    )
    assert (
        "Fetching project page and analyzing links: https://test.pypi.org"
        in result.stdout
    )
    assert (
        'Skipping link: not a file: {}'.format(data.find_links) in
        result.stdout
    ), 'stdout: {}'.format(result.stdout)


@pytest.mark.network
def test_command_line_appends_correctly(script, data):
    """
    Test multiple appending options set by environmental variables.

    """
    script.environ['PIP_FIND_LINKS'] = (
        'https://test.pypi.org {data.find_links}'.format(**locals())
    )
    result = script.pip(
        'install', '-vvv', 'INITools', '--trusted-host',
        'test.pypi.org',
    )

    assert (
        "Fetching project page and analyzing links: https://test.pypi.org"
        in result.stdout
    ), result.stdout
    assert (
        'Skipping link: not a file: {}'.format(data.find_links) in
        result.stdout
    ), 'stdout: {}'.format(result.stdout)


def test_config_file_override_stack(
    script, virtualenv, mock_server, shared_data
):
    """
    Test config files (global, overriding a global config with a
    local, overriding all with a command line flag).
    """
    mock_server.set_responses([
        package_page({}),
        package_page({}),
        package_page({"INITools-0.2.tar.gz": "/files/INITools-0.2.tar.gz"}),
        file_response(shared_data.packages.joinpath("INITools-0.2.tar.gz")),
    ])
    mock_server.start()
    base_address = "http://{}:{}".format(mock_server.host, mock_server.port)

    config_file = script.scratch_path / "test-pip.cfg"

    # set this to make pip load it
    script.environ['PIP_CONFIG_FILE'] = str(config_file)

    config_file.write_text(textwrap.dedent("""\
        [global]
        index-url = {}/simple1
        """.format(base_address)))
    script.pip('install', '-vvv', 'INITools', expect_error=True)
    virtualenv.clear()

    config_file.write_text(textwrap.dedent("""\
        [global]
        index-url = {address}/simple1
        [install]
        index-url = {address}/simple2
        """.format(address=base_address))
    )
    script.pip('install', '-vvv', 'INITools', expect_error=True)
    script.pip(
        'install', '-vvv', '--index-url', "{}/simple3".format(base_address),
        'INITools',
    )

    mock_server.stop()
    requests = mock_server.get_requests()
    assert len(requests) == 4
    assert requests[0]["PATH_INFO"] == "/simple1/initools/"
    assert requests[1]["PATH_INFO"] == "/simple2/initools/"
    assert requests[2]["PATH_INFO"] == "/simple3/initools/"
    assert requests[3]["PATH_INFO"] == "/files/INITools-0.2.tar.gz"


def test_options_from_venv_config(script, virtualenv):
    """
    Test if ConfigOptionParser reads a virtualenv-local config file

    """
    from pip._internal.configuration import CONFIG_BASENAME
    conf = "[global]\nno-index = true"
    ini = virtualenv.location / CONFIG_BASENAME
    with open(ini, 'w') as f:
        f.write(conf)
    result = script.pip('install', '-vvv', 'INITools', expect_error=True)
    assert "Ignoring indexes:" in result.stdout, str(result)
    msg = "DistributionNotFound: No matching distribution found for INITools"
    # Case insensitive as the new resolver canonicalises the project name
    assert msg.lower() in result.stdout.lower(), str(result)


def test_install_no_binary_via_config_disables_cached_wheels(
        script, data, with_wheel):
    config_file = tempfile.NamedTemporaryFile(mode='wt', delete=False)
    try:
        script.environ['PIP_CONFIG_FILE'] = config_file.name
        config_file.write(textwrap.dedent("""\
            [global]
            no-binary = :all:
            """))
        config_file.close()
        res = script.pip(
            'install', '--no-index', '-f', data.find_links,
            'upper', expect_stderr=True)
    finally:
        os.unlink(config_file.name)
    assert "Successfully installed upper-2.0" in str(res), str(res)
    # No wheel building for upper, which was blacklisted
    assert "Building wheel for upper" not in str(res), str(res)
    # Must have used source, not a cached wheel to install upper.
    assert "Running setup.py install for upper" in str(res), str(res)


def test_prompt_for_authentication(script, data, cert_factory):
    """Test behaviour while installing from a index url
    requiring authentication
    """
    cert_path = cert_factory()
    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    ctx.load_cert_chain(cert_path, cert_path)
    ctx.load_verify_locations(cafile=cert_path)
    ctx.verify_mode = ssl.CERT_REQUIRED

    server = make_mock_server(ssl_context=ctx)
    server.mock.side_effect = [
        package_page({
            "simple-3.0.tar.gz": "/files/simple-3.0.tar.gz",
        }),
        authorization_response(str(data.packages / "simple-3.0.tar.gz")),
    ]

    url = "https://{}:{}/simple".format(server.host, server.port)

    with server_running(server):
        result = script.pip('install', "--index-url", url,
                            "--cert", cert_path, "--client-cert", cert_path,
                            'simple', expect_error=True)

    assert 'User for {}:{}'.format(server.host, server.port) in \
           result.stdout, str(result)


def test_do_not_prompt_for_authentication(script, data, cert_factory):
    """Test behaviour if --no-input option is given while installing
    from a index url requiring authentication
    """
    cert_path = cert_factory()
    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    ctx.load_cert_chain(cert_path, cert_path)
    ctx.load_verify_locations(cafile=cert_path)
    ctx.verify_mode = ssl.CERT_REQUIRED

    server = make_mock_server(ssl_context=ctx)

    server.mock.side_effect = [
        package_page({
            "simple-3.0.tar.gz": "/files/simple-3.0.tar.gz",
        }),
        authorization_response(str(data.packages / "simple-3.0.tar.gz")),
    ]

    url = "https://{}:{}/simple".format(server.host, server.port)

    with server_running(server):
        result = script.pip('install', "--index-url", url,
                            "--cert", cert_path, "--client-cert", cert_path,
                            '--no-input', 'simple', expect_error=True)

    assert "ERROR: HTTP error 401" in result.stderr
