"""Structural tests for #3996 — close idle SSE on hidden tabs to free the
HTTP/1.1 connection pool (#3992), plus the reopen-on-re-show correctness fix.

Two idle persistent SSE connections per window (gateway stream + per-session
stream) plus the always-present session-events stream meant 2 windows x 3 = 6 =
the browser's per-origin HTTP/1.1 connection limit, so any subsequent fetch()
queued behind a saturated pool and timed out. #3996 adds Page Visibility API
hooks that close those streams while the tab is hidden and reopen them on
re-show, mirroring the existing ensureSessionEventsSSE() pattern.

These are source-grep checks (the hooks live in static JS with no server round
trip). The key regression guard is that the *per-session* stream actually
reopens on re-show: stopSessionStream() nulls _sessionStreamSessionId, so the
reopen path must NOT depend on that variable (it captures the id into a
dedicated _sessionStreamHiddenSid before closing).
"""

from __future__ import annotations

from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[1]
MESSAGES_JS = (REPO_ROOT / "static" / "messages.js").read_text(encoding="utf-8")
SESSIONS_JS = (REPO_ROOT / "static" / "sessions.js").read_text(encoding="utf-8")


def test_gateway_sse_has_visibility_hook():
    """startGatewaySSE installs a visibilitychange hook that closes on hide."""
    assert "_hermesGatewaySSEVisibilityHook" in SESSIONS_JS
    # Closes on hide, and skips opening while hidden to save pool slots.
    assert "stopGatewaySSE()" in SESSIONS_JS
    start_idx = SESSIONS_JS.find("function startGatewaySSE()")
    assert start_idx != -1
    block = SESSIONS_JS[start_idx:start_idx + 1200]
    assert "visibilitychange" in block
    assert "document.hidden" in block


def test_session_stream_has_visibility_hook():
    """startSessionStream installs a visibilitychange hook."""
    assert "_hermesSessionStreamVisibilityHook" in MESSAGES_JS
    start_idx = MESSAGES_JS.find("function startSessionStream(sid)")
    assert start_idx != -1
    block = MESSAGES_JS[start_idx:start_idx + 2600]
    assert "visibilitychange" in block
    assert "document.hidden" in block
    # Must skip opening a new EventSource while the tab is hidden.
    assert "if (typeof document !== 'undefined' && document.hidden) {" in block


def test_session_stream_reopens_from_dedicated_var_not_nulled_id():
    """Regression: the per-session SSE must reopen on re-show.

    stopSessionStream() sets _sessionStreamSessionId = null, so the visibility
    reopen path must capture the id into a dedicated holder BEFORE closing and
    reopen from that — otherwise the stream closes on hide and never comes back.
    """
    # Dedicated holder declared at module scope.
    assert "_sessionStreamHiddenSid" in MESSAGES_JS, (
        "expected a dedicated holder var for the hidden-tab session id"
    )

    start_idx = MESSAGES_JS.find("function startSessionStream(sid)")
    block = MESSAGES_JS[start_idx:start_idx + 2200]

    # On hide: capture the id, then stop.
    assert "_sessionStreamHiddenSid = _sessionStreamSessionId" in block
    # On re-show: reopen from the captured holder, NOT from the nulled id.
    assert "else if (_sessionStreamHiddenSid)" in block
    assert "void startSessionStream(resumeSid)" in block or \
           "startSessionStream(_sessionStreamHiddenSid)" in block

    # Guard against the buggy form that reopens off the (already-nulled) id.
    assert "else if (_sessionStreamSessionId) {\n        void startSessionStream(_sessionStreamSessionId)" not in block, (
        "reopen must not depend on _sessionStreamSessionId — stopSessionStream() nulls it"
    )


def test_stop_session_stream_still_nulls_session_id():
    """Confirms the precondition the reopen fix defends against."""
    stop_idx = MESSAGES_JS.find("function stopSessionStream()")
    assert stop_idx != -1
    block = MESSAGES_JS[stop_idx:stop_idx + 400]
    assert "_sessionStreamSessionId = null" in block


def test_session_stream_hidden_open_preserves_id_for_reopen():
    """A session opened while the tab is ALREADY hidden must still reopen on re-show.

    Codex CORE: startSessionStream(sid) sets _sessionStreamSessionId and then
    returns early when document.hidden. If it doesn't also record the id in the
    dedicated holder, the visibility handler (which reopens only from the holder)
    never reattaches — silently dropping bg_task_complete / server_turn_started
    for a session loaded in a background tab. The hidden-skip path must set
    _sessionStreamHiddenSid = sid before returning.
    """
    start_idx = MESSAGES_JS.find("function startSessionStream(sid)")
    block = MESSAGES_JS[start_idx:start_idx + 2800]
    # Find the hidden-tab early-return skip path (distinct from the in-hook
    # `if (document.hidden)` branch) and assert it preserves the id.
    hidden_idx = block.find("!== 'undefined' && document.hidden) {")
    assert hidden_idx != -1, "expected a braced hidden-tab skip block (not a bare return)"
    hidden_block = block[hidden_idx:hidden_idx + 160]
    assert "_sessionStreamHiddenSid = sid" in hidden_block, (
        "hidden-tab skip must record the pending session id for reopen on re-show"
    )
