import itertools
import os
import time
from pathlib import Path

from clarabot_nano_common.server.general.handler.filesystem.utility import ls_entry_size, list_directory
from clarabot_nano_common.utility.serialize import msgpack_dumps

from clarabot_nano_tests.testing.file import temp_path


def test_server_list_directory(tmpdir):
    r = Path(str(temp_path(tmpdir)))
    r.mkdir()
    f0 = r / "f0"
    f1 = r / "f1"
    d0 = r / "d0"
    d1 = r / "d1"
    d2 = r / "d2"
    f0.write_text("yes0")
    time.sleep(0.01)
    f1.write_text("yes1")
    time.sleep(0.01)
    d0.mkdir()
    time.sleep(0.01)
    d1.mkdir()
    time.sleep(0.01)
    d2.mkdir()

    ld0 = r / "ld0"
    lf0 = r / "lf0"
    ld1 = r / "ld1"
    lf1 = r / "lf1"
    lfx = r / "lfx"

    os.symlink(d0, ld0, target_is_directory=True)
    time.sleep(0.01)
    os.symlink(f0, lf0, target_is_directory=False)
    time.sleep(0.01)
    os.symlink(d1, ld1, target_is_directory=True)
    time.sleep(0.01)
    os.symlink(f1, lf1, target_is_directory=False)
    time.sleep(0.01)
    os.symlink(f1, lfx, target_is_directory=False)

    d0.rmdir()
    f0.unlink()

    def _dir_iter():
        for p in r.iterdir():
            if p == d2:  # test unresolved by policy
                yield p, None, None
            else:
                s = p.lstat()
                if p == lfx:  # test unresolved link by policy
                    p.resolve(strict=True)  # test resolvable otherwise
                    yield p, s, None
                else:
                    try:
                        yield p, s, p.resolve(strict=True)
                    except OSError:
                        yield p, s, None

    assert (tmp := sorted(list_directory(_dir_iter, image_buckets={f1.name: 117}), key=lambda x: x["name"])) == [
        {"name": d1.name, "mtime": d1.stat().st_mtime},
        {"name": d2.name, "unresolved": True},
        {"name": f1.name, "mtime": f1.stat().st_mtime, "size": 4, "imgb": 117},
        {"name": ld0.name, "symlink": True, "unresolved": True},
        {"name": ld1.name, "symlink": True, "mtime": d1.stat().st_mtime},
        {"name": lf0.name, "symlink": True, "unresolved": True},
        {"name": lf1.name, "symlink": True, "mtime": f1.stat().st_mtime, "size": 4},
        {"name": lfx.name, "symlink": True, "unresolved": True},
    ], str(tmp)


def test_server_ls_entry_size():
    names = ["", ".yes", "please", "ÚNÍCÓDÉ" * 10, "verylongőŐ" * 20]
    mtimes = [0.0, 6545212.1100376, 1510389156.1100376, 1611389156.1100376, 1911389156.1100376, 5911389156.1100376]
    sizes = [0, 1, 100, 200, 800, 20000, 500000, 1000000, 9111000, 271333444, 2888444222, 48000111222, 1222000333444]

    refs = list({"name": x[0], "mtime": x[1], "size": x[2]} for x in itertools.product(names, mtimes, sizes))
    refs.extend({"name": x[0], "mtime": x[1]} for x in itertools.product(names, mtimes))
    real_sizes = []
    for ref in refs:
        real_sizes.append(len(msgpack_dumps(ref)))

    for ref, real_size in zip(refs, real_sizes):
        approx_size = ls_entry_size(ref)
        assert approx_size - 9 <= real_size <= approx_size + 6, f"{real_size}\n{ref}"
