"""Landing zone file I/O helpers for extraction pipelines."""

import hashlib
from pathlib import Path


def landing_path(landing_dir: str | Path, *parts: str) -> Path:
    """Return path to a subdirectory of landing_dir, creating it if absent.

    Example:
        dest_dir = landing_path("data/landing", "psd", "2025", "07")
        # Returns Path("data/landing/psd/2025/07"), all directories created.

    Use this instead of manually calling mkdir — it makes the intent clear
    and ensures the directory always exists before the caller writes to it.
    """
    assert landing_dir, "landing_dir must not be empty"
    path = Path(landing_dir).joinpath(*parts)
    path.mkdir(parents=True, exist_ok=True)
    return path


def content_hash(data: bytes, prefix_bytes: int = 8) -> str:
    """Return the first prefix_bytes hex chars of the SHA256 digest of data.

    Used as a short idempotency key for content-addressed filenames. Two runs
    that produce identical data will produce the same hash, so the file already
    existing on disk is proof the content is unchanged.

    prefix_bytes=8 gives 32-bit collision resistance — sufficient for a few
    thousand files per extractor. Increase to 16 for very high-volume sources.
    """
    assert data, "data must not be empty"
    assert 1 <= prefix_bytes <= 64, f"prefix_bytes must be 1-64, got {prefix_bytes}"
    return hashlib.sha256(data).hexdigest()[:prefix_bytes]


def write_bytes_atomic(path: Path, data: bytes) -> int:
    """Write data to path atomically via a .tmp sibling file.

    Writes to {path}.tmp first, then renames to path. On Linux, rename() is
    atomic when src and dst are on the same filesystem — a reader never sees
    a partial file. Returns the number of bytes written.

    Callers are responsible for compression (e.g. gzip.compress) before calling
    this function.
    """
    assert data, "data must not be empty"
    tmp = path.with_suffix(path.suffix + ".tmp")
    tmp.write_bytes(data)
    tmp.rename(path)
    assert path.exists(), f"File was not written: {path}"
    return len(data)
