"""Tests for memory + screenshot capture wrappers (Phase 17, FN-A4).

Post-D-G22 the capture functions take injected callables (``dump_fn`` /
``screenshot_fn``) + a ``vm_name`` instead of a ``VMProvider`` + handle —
the app binds the callables to the libvirt acquisition escape hatch.
"""

from __future__ import annotations

from datetime import UTC, datetime
from typing import TYPE_CHECKING

from mgf.vm.forensics import (
    ArtifactKind,
    capture_memory,
    capture_screenshot,
)

if TYPE_CHECKING:
    from pathlib import Path

_PNG = b"\x89PNG\r\n\x1a\n" + b"\x00" * 16


def _fake_dump(received: list[bool]):
    """Return a ``dump_fn`` that writes a fake core dump and records ``live``."""

    def _dump(dest: Path, live: bool) -> None:
        received.append(live)
        dest.write_bytes(b"ELFCORE")

    return _dump


def _fake_screenshot(dest: Path) -> None:
    dest.write_bytes(_PNG)


class TestCaptureMemory:
    def test_writes_dump_under_case_memory_dir(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        artifact = capture_memory(_fake_dump([]), "vm", case_dir=case_dir)
        assert artifact.kind == ArtifactKind.MEMORY_DUMP
        assert artifact.path.parent == case_dir / "memory"
        assert artifact.path.exists()
        assert artifact.size_bytes > 0
        assert artifact.sha256 != ""

    def test_default_filename_has_timestamp(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        when = datetime(2026, 4, 26, 12, 0, tzinfo=UTC)
        artifact = capture_memory(_fake_dump([]), "vm", case_dir=case_dir, captured_at=when)
        assert "20260426T120000" in artifact.path.name

    def test_explicit_name_used(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        artifact = capture_memory(_fake_dump([]), "vm", case_dir=case_dir, name="custom.dump")
        assert artifact.path.name == "custom.dump"

    def test_metadata_and_live_flag_propagate(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        seen: list[bool] = []
        artifact = capture_memory(_fake_dump(seen), "suspect-1", case_dir=case_dir, live=False)
        assert seen == [False]  # the live flag reaches the dump callable
        assert artifact.metadata["live"] is False
        assert artifact.metadata["vm_name"] == "suspect-1"


class TestCaptureScreenshot:
    def test_writes_png_under_screenshots_dir(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        artifact = capture_screenshot(_fake_screenshot, "vm", case_dir=case_dir)
        assert artifact.kind == ArtifactKind.SCREENSHOT
        assert artifact.path.parent == case_dir / "screenshots"
        assert artifact.path.read_bytes()[:4] == b"\x89PNG"

    def test_default_filename_has_timestamp(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        when = datetime(2026, 4, 26, 14, 30, tzinfo=UTC)
        artifact = capture_screenshot(_fake_screenshot, "vm", case_dir=case_dir, captured_at=when)
        assert "20260426T143000" in artifact.path.name

    def test_explicit_name(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        artifact = capture_screenshot(_fake_screenshot, "vm", case_dir=case_dir, name="login.png")
        assert artifact.path.name == "login.png"

    def test_metadata_includes_vm_name(self, tmp_path: Path) -> None:
        case_dir = tmp_path / "case"
        case_dir.mkdir()
        artifact = capture_screenshot(_fake_screenshot, "suspect-1", case_dir=case_dir)
        assert artifact.metadata["vm_name"] == "suspect-1"
