"""M3 presence: connection counts with a grace window, announced on the
bus. The strip renders presence as a glow on avatars that exist in the
viewer's PROJECTED payload, so invisible players can't render even though
bare ids transit the stream (documented M7 tightening)."""
from __future__ import annotations

import uuid

from app import events


def _drain_and_capture(monkeypatch):
    published = []
    monkeypatch.setattr(events, "publish", lambda evt: published.append(evt))
    return published


def test_connect_announces_once_per_player(monkeypatch):
    published = _drain_and_capture(monkeypatch)
    pid = uuid.uuid4()
    events.presence_connect(pid)
    events.presence_connect(pid)  # second tab: no re-announce
    assert len(published) == 1
    assert published[0]["type"] == "presence.updated"
    assert published[0]["online"] == [str(pid)]
    # cleanup registry for other tests
    events._presence.clear()


def test_disconnect_grace_only_announces_when_really_gone(monkeypatch):
    published = _drain_and_capture(monkeypatch)
    pid = uuid.uuid4()
    events.presence_connect(pid)
    published.clear()

    # Two tabs open; closing one announces nothing.
    events.presence_connect(pid)
    events.presence_disconnect(pid)
    assert published == []

    # Last tab closes: with no loop set, the grace check runs inline.
    monkeypatch.setattr(events, "_loop", None)
    events.presence_disconnect(pid)
    assert [e["type"] for e in published] == ["presence.updated"]
    assert published[-1]["online"] == []
    events._presence.clear()


def test_none_viewer_is_ignored(monkeypatch):
    published = _drain_and_capture(monkeypatch)
    events.presence_connect(None)
    events.presence_disconnect(None)
    assert published == []
