"""LG-15: every `event=` value emitted by the library MUST be registered in
`docs/reference/EVENTS.md`.

"An event name without an EVENTS.md entry is a defect" (LOGGING.md §4.5). This
pins code↔registry parity so a new `event=` string can't ship un-catalogued,
and a registry entry can't go stale.
"""

from __future__ import annotations

import re
from pathlib import Path

_ROOT = Path(__file__).resolve().parents[3]
_PKG = _ROOT / "src" / "mgf" / "common"
_EVENTS_MD = _ROOT / "docs" / "reference" / "EVENTS.md"

_EVENT_IN_CODE = re.compile(r'"event":\s*"([^"]+)"')
_EVENT_IN_REGISTRY = re.compile(r"^### `([^`]+)`", re.MULTILINE)


def _events_in_code() -> set[str]:
    found: set[str] = set()
    for p in _PKG.rglob("*.py"):
        if "__pycache__" in p.parts:
            continue
        found.update(_EVENT_IN_CODE.findall(p.read_text(encoding="utf-8")))
    return found


def _events_in_registry() -> set[str]:
    return set(_EVENT_IN_REGISTRY.findall(_EVENTS_MD.read_text(encoding="utf-8")))


def test_every_code_event_is_registered() -> None:
    unregistered = sorted(_events_in_code() - _events_in_registry())
    assert not unregistered, (
        "LG-15: `event=` values emitted in src but not registered in "
        "docs/reference/EVENTS.md:\n  "
        + "\n  ".join(unregistered)
        + "\nAdd a `### `<event>`` entry (the registry IS the contract)."
    )


def test_events_follow_subject_action_shape() -> None:
    # <subject>.<action>, all lower-case, dot-delimited (LG-15 naming contract).
    bad = sorted(e for e in _events_in_code() if not re.fullmatch(r"[a-z0-9_]+(\.[a-z0-9_]+)+", e))
    assert not bad, f"LG-15: events off the <subject>.<action> shape: {bad}"
