"""Screenshot capture over an injected ``screenshot_fn`` (D-G22).

One-shot capture only in v1 (FN-A4.2). Periodic mode (F-SC-2) is deferred to
Phase 17-B. The screenshot callable is engine-specific (the app binds it to
the libvirt acquisition escape hatch); this module stays provider-free.
"""

from __future__ import annotations

import hashlib
from datetime import UTC, datetime
from typing import TYPE_CHECKING

from mgf.vm.forensics.case import Artifact, ArtifactKind

if TYPE_CHECKING:
    from collections.abc import Callable
    from pathlib import Path

    # ``(dest) -> None`` — write the screenshot image to ``dest``.
    ScreenshotFn = Callable[[Path], None]


__all__ = ["capture_screenshot"]


def capture_screenshot(
    screenshot_fn: ScreenshotFn,
    vm_name: str,
    *,
    case_dir: Path,
    captured_at: datetime | None = None,
    name: str | None = None,
) -> Artifact:
    """Capture a screenshot to ``<case_dir>/screenshots/<name>``.

    ``screenshot_fn(dest)`` performs the engine-specific capture. ``name``
    defaults to a timestamped slug (``screenshot-YYYYmmddTHHMMSS.png``).

    The on-disk format follows libvirt's behaviour — typically PPM on legacy
    hosts, PNG on modern qemu builds. Callers that need a normalised format do
    so as a post-processing step.
    """
    when = captured_at or datetime.now(UTC)
    filename = name or f"screenshot-{when.strftime('%Y%m%dT%H%M%S')}.png"

    screenshots_dir = case_dir / "screenshots"
    screenshots_dir.mkdir(parents=True, exist_ok=True)
    dest = screenshots_dir / filename

    screenshot_fn(dest)

    return Artifact(
        kind=ArtifactKind.SCREENSHOT,
        path=dest,
        sha256=_hash_file(dest),
        size_bytes=dest.stat().st_size,
        captured_at=when,
        captured_by="mgf.vm.forensics.capture.screenshot",
        metadata={"vm_name": vm_name},
    )


def _hash_file(path: Path, *, chunk_size: int = 1 << 16) -> str:
    h = hashlib.sha256()
    with path.open("rb") as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            h.update(chunk)
    return h.hexdigest()
