import os
import sys
from pathlib import Path

import pytest

from clarabot_nano.utility.path.validate import validate_path, ValidationError
from clarabot_nano_tests.utility.path.validate.test_name import (
    PATH_NAMES_VALID_ALL_,
    PATH_NAMES_INVALID_ALL_,
    PATH_NAMES_VALID_POSIX_,
    PATH_NAMES_INVALID_NT_,
    PATH_NAMES_INVALID_NT_DOS_,
    PATH_NAMES_INVALID_NT_EXCEPT_PATH_,
)

if sys.platform.startswith("win"):
    VALID_ROOT_ = Path(r"\\?\c:\tmp\x")
else:
    VALID_ROOT_ = Path("/tmp/x")
PATHS_VALID_ALL_ = [
    "asd",
    "asd/xy",
    "x/y",
    "asd/x y.ye .s",
    r"\\?\c:\temp\test-file.txt",
    r"\\?\UnC\LOCAlhoST\c$\temp\test-file.txt",
]
PATHS_INVALID_ALL_ = [
    "asd/xy\x00",
    "",
    ".",
    "..",
    "./.",
    "./..",
    "../..",
    "x//y",
    "./.a../..s/.d.f./qwer/rtzuiX./.vb.m./",
    "/.a../..s/.d.f./qwer/rtzuiX./.vb.m./.",
    "../.a../..s/.d.f./qwer/rtzuiX./.vb.m./",
    "/.a../..s/.d.f./qwer/rtzuiX./.vb.m./..",
    "/.a././..s/.d.f./qwer/rtzuiX./.vb.m./",
    "/.a..//..s/.d.f./qwer/rtzuiX./.vb.m./",
    "/.a../../s/.d.f./qwer/rtzuiX./.vb.m./",
    "/.",
    "/./",
    "/../",
    "/.a../..s/.d.f./qwer/rtz\x00uiX./.vb.m./",
    "\x00",
]
PATHS_VALID_POSIX_ = [
    ".a../..s/.d.f./qwer/rtzuiX./.vb.m.",
    "/.a../..s/.d.f./qwer/rtzuiX./.vb.m.",
    ".a../..s/.d.f./qwer/rtzuiX./.vb.m./",
    "/.a../..s/.d.f./qwer/rtzuiX./.vb.m./",
    "/.a./.../s/.d.f./qwer/rtzuiX./.vb.m./",
    "/",
    "/.a",
    "/ /a",
    "x/ /a",
    r"\\.\UNC\LOCALHOST\c$\temp\test-file.txt",
    r"\\LOCALHOST\c$\temp\test-file.txt",
    r"c:\temp\test-file.txt",
    r"\temp\test-file.txt",
    r"c:temp\test-file.txt",
    r"\\?\c:temp\test-file.txt",
]


def _validate_path_sets(valid, invalid):
    for v_n in valid:
        try:
            validate_path(v_n)
        except ValidationError as e:
            raise AssertionError(f"{v_n}") from e
    for i_n in invalid:
        with pytest.raises(ValidationError):
            validate_path(i_n)


def _create_paths_from_names(names, filter_fn=None):
    paths = []
    for name in names:
        if filter_fn is None or filter_fn(name, False):
            paths.append(os.path.sep.join([str(VALID_ROOT_), name]))
        if filter_fn is None or filter_fn(name, True):
            paths.append(os.path.sep.join([str(VALID_ROOT_), name, "asd"]))
    return paths


def test_utility_path_validate_path_universal():
    validate_path(VALID_ROOT_)
    _validate_path_sets(PATHS_VALID_ALL_, PATHS_INVALID_ALL_)
    _validate_path_sets(
        _create_paths_from_names(PATH_NAMES_VALID_ALL_),
        _create_paths_from_names(PATH_NAMES_INVALID_ALL_, lambda n, _: "/" not in n),
    )


@pytest.mark.skipif(sys.platform.startswith("win"), reason="for posix only")
def test_utility_path_validate_path_posix():
    _validate_path_sets(
        PATHS_VALID_POSIX_
        + _create_paths_from_names(PATH_NAMES_INVALID_NT_DOS_)
        + _create_paths_from_names(PATH_NAMES_INVALID_NT_),
        [],
    )


@pytest.mark.skipif(not sys.platform.startswith("win"), reason="for windows only")
def test_utility_path_validate_path_nt():
    for p in [
        "C:/$Volume",
        "//?/c:/$Volume",
        "//?/c:/$Volume/my_place/yes",
        "//?/c:/$Extend/$Config",
        "\\\\?\\c:\\$Extend\\$Config",
        "\\\\?\\c:\\$Extend\\$Config\\good",
        "c:\\$Boot\\here\\yes",
    ]:
        with pytest.raises(ValidationError):
            validate_path(p)
    assert PATH_NAMES_INVALID_NT_EXCEPT_PATH_ == ["asd\\x", "asd\\"]
    _validate_path_sets(
        _create_paths_from_names(PATH_NAMES_INVALID_NT_DOS_),
        PATHS_VALID_POSIX_
        + _create_paths_from_names(PATH_NAMES_VALID_POSIX_)
        + _create_paths_from_names(
            PATH_NAMES_INVALID_NT_,
            lambda n, hp: n not in PATH_NAMES_INVALID_NT_EXCEPT_PATH_
            or (n == PATH_NAMES_INVALID_NT_EXCEPT_PATH_[1] and hp),
        ),
    )
