"""
Assert some basic behaviours about the path resolution of the builtin pathlib for safety and consistency.
1. When resolved, root paths always end with trailing (back)slash.
2. When resolved, non-root paths never end with trailing (back)slash.
3. A resolved path is always absolute and its first part is always the root which ends with trailing (back)slash.
4. Double path-separators are forbidden except for the beginning of DOS device and UNC paths on Windows.
"""
import random
import sys
from pathlib import Path

import pytest


def _check_resolution(test_paths, test_parts, test_string, slash_magic=False):
    path_objects = []
    path_parts = []
    path_strings = []

    def _collect_path(p_):
        po = Path(p_)
        assert po.is_absolute()
        po = po.resolve(strict=True)
        path_objects.append(po)
        path_parts.append(po.parts)
        path_strings.append(po.as_posix())

    for p in test_paths:
        _collect_path(p)
        if slash_magic:
            # play around with the path separators to check safe handling of inconsistent posix/nt separators
            _collect_path(p.replace("\\", "/"))
            _collect_path(p.replace("/", "\\"))
            for _ in range(2):
                mp = []
                for px in p.replace("\\", "/").split("/"):
                    mp.append("/" if random.random() < 0.5 else "\\")
                    mp.append(px)
                _collect_path("".join(mp[1:]))
    assert all(po == path_objects[0] for po in path_objects), f"{path_objects}\n{test_paths}\n{test_string}"
    assert all(pp == test_parts for pp in path_parts), f"{test_parts} {path_parts}\n{test_paths}\n{test_string}"
    assert all(ps == test_string for ps in path_strings), f"{test_string} {path_strings}\n{test_paths}\n{test_string}"


def _adjust_nt_case_to_py_version(test_paths, test_parts, test_string):
    if sys.version_info[:2] < (3, 10):
        return [(test_paths, test_parts, test_string)]
    # NOTE: Since Python 3.10 the extended-length prefix is preserved during resolve.
    #       This is change makes the result of the resolve seemingly inconsistent, but it becomes consistent with
    #       the way os.path.realpath(...) resolves the path actually.
    if test_string.startswith("//") and not test_string.startswith("//.") and not test_string.startswith("//?"):
        test_parts2 = tuple(["\\\\?\\UNC" + test_parts[0][1:], *test_parts[1:]])
        test_string2 = "//?/UNC" + test_string[1:]
    else:
        test_parts2 = tuple(["\\\\?\\" + test_parts[0], *test_parts[1:]])
        test_string2 = "//?/" + test_string
    return [
        ([x for x in test_paths if not x.startswith("//?/")], test_parts, test_string),
        ([x for x in test_paths if x.startswith("//?/")], test_parts2, test_string2),
    ]


@pytest.mark.skipif(not sys.platform.startswith("win"), reason="for windows only")
def test_utility_path_pathlib_nt():
    # NOTE: Paths on Windows... just... just why?
    for p in ["c:", "C:", "\\\\?\\c:", "//?/c:"]:
        assert not Path(p).is_absolute()
    for p in ["/", "//", "///", "\\", "\\\\", "\\\\\\", "\\/", "/\\"]:
        assert not Path(p).is_absolute()
        assert Path(p).resolve(strict=True).as_posix() == "C:/"
    # test normal (drive-letter:\) and DOS device paths (\\.\ and \\?\)
    for test_paths, test_parts, test_string in [
        *_adjust_nt_case_to_py_version(
            [
                "c:/",
                "C:/.",
                "//./c:",  # this one is odd as hell
                "//./c:/",
                "//./c:/.",
                "//?/c:/",
                "//?/c:/.",
            ],
            ("C:\\",),
            "C:/",
        ),
        *_adjust_nt_case_to_py_version(
            [
                "C:/WINDOWS",
                "c:/windows/.",
                "C:/wINDOws/",
                "//./c:/windows",
                "//./C:/wINDOws/",
                "//./c:/windows/.",
                "//?/c:/windows",
                "//?/C:/wINDOws/",
                "//?/c:/windows/.",
            ],
            ("C:\\", "Windows"),
            "C:/Windows",
        ),
        *_adjust_nt_case_to_py_version(
            [
                "C:/WINDOWS/SySTEM32",
                "c:/windows/System32/.",
                "C:/wINDOws/systeM32/",
                "//./c:/windows/SySTem32",
                "//./c:/windows/SySTem32/",
                "//./c:/windows/SySTem32/.",
                "//?/c:/windows/SySTem32",
                "//?/c:/windows/SySTem32/",
                "//?/c:/windows/SySTem32/.",
            ],
            ("C:\\", "Windows", "System32"),
            "C:/Windows/System32",
        ),
    ]:
        _check_resolution(test_paths, test_parts, test_string, slash_magic=True)
    # test UNC paths
    # NOTE: The host and share components are NOT case-corrected because their canonical representation is not up for
    # the local system to decide.
    for test_paths, test_parts, test_string in [
        *_adjust_nt_case_to_py_version(
            [
                "//localHost/c$",
                "//localHost/c$/",
                "//localHost/c$/.",
                "//./UNC/localHost/c$",
                "//./UNC/localHost/c$/",
                "//./UNC/localHost/c$/.",
                "//?/UNC/localHost/c$",
                "//?/UNC/localHost/c$/",
                "//?/UNC/localHost/c$/.",
            ],
            ("\\\\localHost\\c$\\",),
            "//localHost/c$/",
        ),
        *_adjust_nt_case_to_py_version(
            [
                "//localHost/c$/windows",
                "//localHost/c$/windows/",
                "//localHost/c$/windows/.",
                "//./UNC/localHost/c$/windows",
                "//./UNC/localHost/c$/windows/",
                "//./UNC/localHost/c$/windows/.",
                "//?/UNC/localHost/c$/windows",
                "//?/UNC/localHost/c$/windows/",
                "//?/UNC/localHost/c$/windows/.",
            ],
            ("\\\\localHost\\c$\\", "Windows"),
            "//localHost/c$/Windows",
        ),
        *_adjust_nt_case_to_py_version(
            [
                "//localHost/c$/windows/SySTEM32",
                "//localHost/c$/windows/SySTEM32/",
                "//localHost/c$/windows/SySTEM32/.",
                "//./UNC/localHost/c$/windows/SySTEM32",
                "//./UNC/localHost/c$/windows/SySTEM32/",
                "//./UNC/localHost/c$/windows/SySTEM32/.",
                "//?/UNC/localHost/c$/windows/SySTEM32",
                "//?/UNC/localHost/c$/windows/SySTEM32/",
                "//?/UNC/localHost/c$/windows/SySTEM32/.",
            ],
            ("\\\\localHost\\c$\\", "Windows", "System32"),
            "//localHost/c$/Windows/System32",
        ),
    ]:
        _check_resolution(test_paths, test_parts, test_string, slash_magic=True)


@pytest.mark.skipif(sys.platform.startswith("win"), reason="for posix only")
def test_utility_path_pathlib_posix():
    for p in ["c:/", "C:\\", "\\", "\\/"]:
        assert not Path(p).is_absolute()
    for test_paths, test_parts, test_string in [
        (["/", "/."], ("/",), "/"),
        (["/usr", "/usr/."], ("/", "usr"), "/usr"),
        (["/usr/bin", "/usr/bin/."], ("/", "usr", "bin"), "/usr/bin"),
    ]:
        _check_resolution(test_paths, test_parts, test_string)
