import hashlib
import os
import stat
from pathlib import Path
from typing import Generator, List, Dict, Any, Tuple


def list_directory(pg: Generator[Path, None, None], dirs_only: bool = False) -> List[Dict[str, Any]]:
    """
    Create entries of the items in a directory.
    NOTE: if this is changed ls_entry_size() must be modified accordingly!
    """
    es = []
    for p in pg:
        e = {"name": p.name}
        try:
            s = p.stat()
        except OSError:
            e["unresolved"] = True
        else:
            e["mtime"] = s.st_mtime
            if stat.S_ISDIR(s.st_mode):
                pass
            elif dirs_only:
                continue
            else:
                e["size"] = s.st_size
        es.append(e)
    return es


def ls_entry_size(item: dict) -> int:
    """
    To be used with items from list_directory() with _approximate_part_slicing().
    Each field is approximated: (+1 for root)
        name: 6 + actual encoded length of the name
        mtime: 15
        size: 5 + 1, 2, 4 or 8 depending on size (we'll simplify and count as 4)
    NOTE: extra length indicating the size of name is not accounted for.
    """
    return 31 + len(item["name"].encode("utf-8"))


def file_stat(p: Path) -> os.stat:
    return p.stat()


def read_file_range(p: Path, start: int, n: int) -> bytes:
    with open(p, "rb") as f:
        f.seek(start)
        return f.read(n)


def fingerprint_file_range(p: Path, start: int, n: int, times: int, salt: bytes) -> Tuple[bytes, int]:
    # TODO: use HMAC, CMAC or Poly1305
    h = hashlib.sha512(salt)
    c = 0
    with open(p, "rb") as f:
        f.seek(start)
        for _ in range(times):
            b = f.read(n)
            c += len(b)
            h.update(b)
    return h.digest()[:32], c
