"""Pin PUBLIC_API.json + PUBLIC_API.md against the live source surface.

DOC-08 / AP-10: ``PUBLIC_API.json`` MUST be generated from ``__all__``
and pinned in CI; ``PUBLIC_API.md`` MUST track the same names. If a
public symbol is added/removed without regenerating, this test fails —
forcing the contract docs to stay in lockstep with the code.

The generator extracts ``__all__`` statically (AST) from this repo's
``src/`` so the pin describes THIS repo's surface even while the
transitional ``mgf-vm`` umbrella shadows ``mgf.vm.forensics`` at import
time (see ``tools/generate_public_api_json.py``).
"""

from __future__ import annotations

import importlib.util
import json
from pathlib import Path

_REPO_ROOT = Path(__file__).resolve().parents[5]
_PUBLIC_API_JSON = _REPO_ROOT / "PUBLIC_API.json"
_PUBLIC_API_MD = _REPO_ROOT / "PUBLIC_API.md"
_GENERATOR = _REPO_ROOT / "tools" / "generate_public_api_json.py"


def _load_generator():  # type: ignore[no-untyped-def]
    spec = importlib.util.spec_from_file_location("_gen_public_api", _GENERATOR)
    assert spec is not None
    assert spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def test_public_api_json_matches_generator() -> None:
    """The committed JSON MUST be byte-identical to a fresh generation."""
    gen = _load_generator()
    expected = gen.render(gen.build_payload())
    actual = _PUBLIC_API_JSON.read_text(encoding="utf-8")
    assert actual == expected, (
        "PUBLIC_API.json is stale — run "
        "`uv run python tools/generate_public_api_json.py` and commit."
    )


def test_public_api_md_lists_every_name() -> None:
    """Every name in the JSON contract MUST appear in PUBLIC_API.md."""
    payload = json.loads(_PUBLIC_API_JSON.read_text(encoding="utf-8"))
    md = _PUBLIC_API_MD.read_text(encoding="utf-8")
    all_names: set[str] = set()
    for names in payload["modules"].values():
        all_names.update(names)
    # __version__ is documented as a row, not by bare name match.
    all_names.discard("__version__")
    missing = sorted(n for n in all_names if n not in md)
    assert not missing, f"PUBLIC_API.md is missing public names: {missing}"


def test_generator_version_matches_package() -> None:
    """The JSON __version__ MUST equal the package __version__."""
    payload = json.loads(_PUBLIC_API_JSON.read_text(encoding="utf-8"))
    init_text = (_REPO_ROOT / "src" / "mgf" / "vm" / "forensics" / "__init__.py").read_text(
        encoding="utf-8"
    )
    assert f'__version__ = "{payload["__version__"]}"' in init_text
