import itertools
import os
import stat
import sys
import time
from pathlib import Path
from typing import Tuple, Optional

from clarabot_nano_common.utility.file import stat_equals, path_stat_equals, stat_differs, path_stat_differs
from clarabot_nano_common.utility.path.common import normalize_absolute_path

from clarabot_nano.utility.file import DRIVE_PUT_FILE
from clarabot_nano_tests.testing.file import temp_path


def test_utility_file_drive_put_file_reserve(tmpdir):
    root = Path(normalize_absolute_path(temp_path(tmpdir)))
    root.mkdir()

    for name in ["asdf", "asdf.txt", "asdf.yes.txt"]:
        f = root / name
        tmp = DRIVE_PUT_FILE.reserve(f)
        assert (root / tmp).is_file()
        assert DRIVE_PUT_FILE.tag_re.match(tmp) is not None


def test_path_stat_comparators(tmpdir):
    root = Path(normalize_absolute_path(temp_path(tmpdir)))
    root.mkdir()

    x0 = root / "x0"
    y0 = root / "y0"
    y0.mkdir()

    f0 = root / "f0"
    lf = root / "lf"
    d0 = root / "d0"
    ld = root / "ld"
    lb = root / "lb"

    f0.write_bytes(b"asdf")
    lf.symlink_to(f0.name)
    d0.mkdir()
    ld.symlink_to(d0.name, target_is_directory=True)
    lb.symlink_to("_ no t here_")

    def _check_eq_neq(
        a_: Tuple[Path, Optional[os.stat_result]], b_: Tuple[Path, Optional[os.stat_result]], eq: bool, p_eq: bool
    ):
        for a, b in [(a_, b_), (b_, a_)]:
            assert stat_equals(a[1], b[1]) is eq
            assert path_stat_equals(a[0], a[1], b[0], b[1]) is (eq and p_eq)
            assert stat_differs(a[1], b[1]) is (not eq)
            assert path_stat_differs(a[0], a[1], b[0], b[1]) is (not eq or not p_eq)

    ps = [f0, lf, d0, ld, lb]
    ss = {}
    for p in ps:
        ss[p] = p.lstat()
        _check_eq_neq((p, ss[p]), (p, p.lstat()), True, True)
        _check_eq_neq((p, ss[p]), (x0, p.lstat()), True, False)
        _check_eq_neq((p, ss[p]), (p, None), False, True)
        _check_eq_neq((p, ss[p]), (x0, y0.lstat()), False, False)
        _check_eq_neq((p, None), (p, None), True, True)
        _check_eq_neq((p, None), (x0, None), True, False)

    for p0, p1 in itertools.combinations(ps, 2):
        for s0, s1 in [(ss[p0], ss[p1]), (ss[p0], p1.lstat()), (p0.lstat(), ss[p1]), (p0.lstat(), p1.lstat())]:
            _check_eq_neq((p0, s0), (p1, s1), False, True)
            _check_eq_neq((p0, s0), (x0, s1), False, False)

    if sys.platform.startswith("win"):
        # On Windows the NTFS filesystem does not store the full nano-second timestamp, so  very close sequential
        # changes may not produce different stats (mtimes).
        # NOTE: Even though this is sub-optimal, the directory caching does not break because of it, thanks to the
        # indexer managing a custom mtime for each directory in the drive.
        time.sleep(0.0001)
    # change each and observe
    for p in ps:
        if stat.S_ISLNK(ss[p].st_mode):
            p.unlink()
            p.symlink_to(lb.name)
        elif stat.S_ISDIR(ss[p].st_mode):
            (p / "x").mkdir()
        else:
            p.write_bytes(b"xyz")

        _check_eq_neq((p, ss[p]), (p, p.lstat()), False, True)
