"""Path-containment helper for code that joins attacker-controlled names
under a server-owned root.
"""

from __future__ import annotations

from pathlib import Path


def safe_join(root: Path, name: str) -> Path | None:
    """Resolve ``name`` under ``root`` and return the resolved Path, or
    ``None`` if it would escape ``root`` or is unrepresentable.

    Rejects:
      * empty names
      * paths that resolve outside ``root`` (``..`` traversal, absolute paths)
      * paths the OS can't resolve (embedded NULs, OSError on stat)

    Normalizes:
      * backslash separators to forward slash so a Windows-style entry
        name inside a user-supplied archive can't bypass containment on
        POSIX hosts (``..\\foo`` would otherwise be treated as a literal
        single filename on Linux and resolve inside ``root`` — but on
        Windows the same string IS a traversal; normalising means both
        platforms reject it identically).
    """
    if not name:
        return None
    safe = name.replace("\\", "/")
    try:
        root_resolved = root.resolve()
        candidate = (root_resolved / safe).resolve()
        if not candidate.is_relative_to(root_resolved):
            return None
    except (ValueError, OSError):
        return None
    return candidate
