"""Read-only package preparation and source-closure inventory."""

from __future__ import annotations

import hashlib
import os
import stat
from pathlib import Path

from .contract import FilePin


class PreparationError(RuntimeError):
    """A proposed package input is unsafe to seal."""


def pin_file(path: Path, *, expected_mode: int) -> FilePin:
    """Capture a regular no-follow single-link file for a draft package."""
    descriptor = os.open(path, os.O_RDONLY | os.O_CLOEXEC | os.O_NOFOLLOW | os.O_NONBLOCK)
    try:
        info = os.fstat(descriptor)
        named = os.stat(path, follow_symlinks=False)
        if (
            not stat.S_ISREG(info.st_mode)
            or stat.S_IMODE(info.st_mode) != expected_mode
            or info.st_nlink != 1
            or (info.st_dev, info.st_ino) != (named.st_dev, named.st_ino)
        ):
            raise PreparationError("unsafe package input")
        chunks: list[bytes] = []
        while chunk := os.read(descriptor, 65_536):
            chunks.append(chunk)
        return FilePin(
            path=path,
            sha256=hashlib.sha256(b"".join(chunks)).hexdigest(),
            device=info.st_dev,
            inode=info.st_ino,
            uid=info.st_uid,
            gid=info.st_gid,
            mode=expected_mode,
            nlink=1,
            size=info.st_size,
        )
    finally:
        os.close(descriptor)
