from __future__ import annotations

import itertools
import sys
from shutil import copy2

import pytest

from virtualenv.run import cli_run
from virtualenv.seed.embed.pip_invoke import PipInvoke
from virtualenv.seed.wheels.bundle import load_embed_wheel
from virtualenv.seed.wheels.embed import BUNDLE_FOLDER, BUNDLE_SUPPORT


@pytest.mark.slow
@pytest.mark.parametrize("no", ["pip", "setuptools", "wheel", ""])
def test_base_bootstrap_via_pip_invoke(tmp_path, coverage_env, mocker, current_fastest, no):  # noqa: C901
    extra_search_dir = tmp_path / "extra"
    extra_search_dir.mkdir()
    for_py_version = f"{sys.version_info.major}.{sys.version_info.minor}"
    new = BUNDLE_SUPPORT[for_py_version]
    for wheel_filename in BUNDLE_SUPPORT[for_py_version].values():
        copy2(str(BUNDLE_FOLDER / wheel_filename), str(extra_search_dir))

    def _load_embed_wheel(app_data, distribution, for_py_version, version):  # noqa: ARG001
        return load_embed_wheel(app_data, distribution, old_ver, version)

    old_ver = "3.8"
    old = BUNDLE_SUPPORT[old_ver]
    mocker.patch("virtualenv.seed.wheels.bundle.load_embed_wheel", side_effect=_load_embed_wheel)

    def _execute(cmd, env):
        expected = set()
        for distribution, with_version in versions.items():
            if distribution == no:
                continue
            if with_version == "embed" or old[distribution] == new[distribution]:
                expected.add(BUNDLE_FOLDER)
            else:
                expected.add(extra_search_dir)
        expected_list = list(
            itertools.chain.from_iterable(["--find-links", str(e)] for e in sorted(expected, key=str)),
        )
        found = cmd[-len(expected_list) :] if expected_list else []
        assert "--no-index" not in cmd
        cmd.append("--no-index")
        assert found == expected_list
        return original(cmd, env)

    original = PipInvoke._execute  # noqa: SLF001
    run = mocker.patch.object(PipInvoke, "_execute", side_effect=_execute)
    versions = {"pip": "embed", "setuptools": "bundle", "wheel": new["wheel"].split("-")[1]}

    create_cmd = [
        "--seeder",
        "pip",
        str(tmp_path / "env"),
        "--download",
        "--creator",
        current_fastest,
        "--extra-search-dir",
        str(extra_search_dir),
        "--app-data",
        str(tmp_path / "app-data"),
    ]
    for dist, version in versions.items():
        create_cmd.extend([f"--{dist}", version])
    if no:
        create_cmd.append(f"--no-{no}")
    result = cli_run(create_cmd)
    coverage_env()

    assert result
    assert run.call_count == 1

    site_package = result.creator.purelib
    pip = site_package / "pip"
    setuptools = site_package / "setuptools"
    wheel = site_package / "wheel"
    files_post_first_create = list(site_package.iterdir())

    if no:
        no_file = locals()[no]
        assert no not in files_post_first_create

    for key in ("pip", "setuptools", "wheel"):
        if key == no:
            continue
        assert locals()[key] in files_post_first_create
