# SPDX-License-Identifier: MIT

import contextlib
import os
import os.path
import pathlib
import re
import shutil
import subprocess
import sys
import tempfile

from venv import EnvBuilder

import pytest

import mesonpy


def adjust_packaging_platform_tag(platform: str) -> str:
    # The packaging module generates overly specific platforms tags on
    # Linux.  The platforms tags on Linux evolved over time.
    # meson-python uses more relaxed platform tags to maintain
    # compatibility with old wheel installation tools.  The relaxed
    # platform tags match the ones generated by the wheel package.
    # https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/
    return re.sub(r'^(many|musl)linux(1|2010|2014|_\d+_\d+)_(.*)$', r'linux_\3', platform)


package_dir = pathlib.Path(__file__).parent / 'packages'


@contextlib.contextmanager
def cd_package(package):
    cur_dir = os.getcwd()
    package_path = package_dir / package
    os.chdir(package_path)
    try:
        yield package_path
    finally:
        os.chdir(cur_dir)


@contextlib.contextmanager
def in_git_repo_context(path=os.path.curdir):
    # Resist the tentation of using pathlib.Path here: it is not
    # supporded by subprocess in Python 3.7.
    path = os.path.abspath(path)
    shutil.rmtree(os.path.join(path, '.git'), ignore_errors=True)
    try:
        subprocess.run(['git', 'init', '-b', 'main', path], check=True)
        subprocess.run(['git', 'config', 'user.email', 'author@example.com'], cwd=path, check=True)
        subprocess.run(['git', 'config', 'user.name', 'A U Thor'], cwd=path, check=True)
        subprocess.run(['git', 'add', '*'], cwd=path, check=True)
        subprocess.run(['git', 'commit', '-q', '-m', 'Test'], cwd=path, check=True)
        yield
    finally:
        # PermissionError raised on Windows.
        with contextlib.suppress(PermissionError):
            shutil.rmtree(os.path.join(path, '.git'))


@pytest.fixture(scope='session')
def tmp_dir_session(tmpdir_factory):
    return pathlib.Path(tempfile.mkdtemp(
        prefix='mesonpy-test-',
        dir=tmpdir_factory.mktemp('test'),
    ))


class VEnv(EnvBuilder):
    def __init__(self, env_dir):
        super().__init__(with_pip=True)
        self.create(env_dir)

    def ensure_directories(self, env_dir):
        context = super().ensure_directories(env_dir)
        # Store the path to the venv Python interpreter. There does
        # not seem to be a way to do this without subclassing.
        self.executable = context.env_exe
        return context


@pytest.fixture()
def venv():
    path = pathlib.Path(tempfile.mkdtemp(prefix='mesonpy-test-venv-'))
    venv = VEnv(path)
    try:
        yield venv
    finally:
        shutil.rmtree(path)


def generate_package_fixture(package):
    @pytest.fixture
    def fixture():
        with cd_package(package) as new_path:
            yield new_path
    return fixture


def generate_sdist_fixture(package):
    @pytest.fixture(scope='session')
    def fixture(tmp_dir_session):
        with cd_package(package), in_git_repo_context():
            return tmp_dir_session / mesonpy.build_sdist(tmp_dir_session)
    return fixture


def generate_wheel_fixture(package):
    @pytest.fixture(scope='session')
    def fixture(tmp_dir_session):
        with cd_package(package), in_git_repo_context():
            return tmp_dir_session / mesonpy.build_wheel(tmp_dir_session)
    return fixture


# inject {package,sdist,wheel}_* fixtures (https://github.com/pytest-dev/pytest/issues/2424)
for package in os.listdir(package_dir):
    normalized = package.replace('-', '_')
    globals()[f'package_{normalized}'] = generate_package_fixture(package)
    globals()[f'sdist_{normalized}'] = generate_sdist_fixture(package)
    globals()[f'wheel_{normalized}'] = generate_wheel_fixture(package)


@pytest.fixture(autouse=True, scope='session')
def disable_pip_version_check():
    # Cannot use the 'monkeypatch' fixture because of scope mismatch.
    mpatch = pytest.MonkeyPatch()
    yield mpatch.setenv('PIP_DISABLE_PIP_VERSION_CHECK', '1')
    mpatch.undo()


@pytest.fixture(scope='session')
def pep518_wheelhouse(tmpdir_factory):
    wheelhouse = tmpdir_factory.mktemp('wheelhouse')
    meson_python = str(package_dir.parent.parent)
    # Populate wheelhouse with wheel for the following packages and
    # their dependencies.  Wheels are downloaded from PyPI or built
    # from the source distribution as needed.  Sources or wheels in
    # the pip cache are used when available.
    packages = [
        meson_python,
    ]
    subprocess.run([sys.executable, '-m', 'pip', 'wheel', '--wheel-dir', str(wheelhouse), *packages], check=True)
    return str(wheelhouse)


@pytest.fixture
def pep518(pep518_wheelhouse, monkeypatch):
    monkeypatch.setenv('PIP_FIND_LINKS', pep518_wheelhouse)
    monkeypatch.setenv('PIP_NO_INDEX', 'true')
    return pep518_wheelhouse
