from contextlib import contextmanager
import os.path
from pathlib import Path
from string import ascii_lowercase
import subprocess
import sys
import textwrap

from _pytest.pytester import Pytester


@contextmanager
def subst_path_windows(filepath: Path):
    for c in ascii_lowercase[7:]:  # Create a subst drive from H-Z.
        c += ":"
        if not os.path.exists(c):
            drive = c
            break
    else:
        raise AssertionError("Unable to find suitable drive letter for subst.")

    directory = filepath.parent
    basename = filepath.name

    args = ["subst", drive, str(directory)]
    subprocess.check_call(args)
    assert os.path.exists(drive)
    try:
        filename = Path(drive, os.sep, basename)
        yield filename
    finally:
        args = ["subst", "/D", drive]
        subprocess.check_call(args)


@contextmanager
def subst_path_linux(filepath: Path):
    directory = filepath.parent
    basename = filepath.name

    target = directory / ".." / "sub2"
    os.symlink(str(directory), str(target), target_is_directory=True)
    try:
        filename = target / basename
        yield filename
    finally:
        # We don't need to unlink (it's all in the tempdir).
        pass


def test_link_resolve(pytester: Pytester) -> None:
    """See: https://github.com/pytest-dev/pytest/issues/5965."""
    sub1 = pytester.mkpydir("sub1")
    p = sub1.joinpath("test_foo.py")
    p.write_text(
        textwrap.dedent(
            """
        import pytest
        def test_foo():
            raise AssertionError()
        """
        ),
        encoding="utf-8",
    )

    subst = subst_path_linux
    if sys.platform == "win32":
        subst = subst_path_windows

    with subst(p) as subst_p:
        result = pytester.runpytest(str(subst_p), "-v")
        # i.e.: Make sure that the error is reported as a relative path, not as a
        # resolved path.
        # See: https://github.com/pytest-dev/pytest/issues/5965
        stdout = result.stdout.str()
        assert "sub1/test_foo.py" not in stdout

        # i.e.: Expect drive on windows because we just have drive:filename, whereas
        # we expect a relative path on Linux.
        expect = f"*{subst_p}*" if sys.platform == "win32" else "*sub2/test_foo.py*"
        result.stdout.fnmatch_lines([expect])
