"""No user-visible surface claims the picture is live.

The broadcast runs a deliberate ~22 s presentation delay and most of what airs is replay, so a
`LIVE` badge asserted something the product cannot support. The timestamp in the bottom-left is the
honest disclosure: the viewer reads the time and forms their own view of how old the picture is.
Specified in `docs/streamed-picture-style-guide.md` §8.

This is the guard that stops it coming back. It scans the rendered TEXT of every user-facing surface
— not the source's identifiers — because the internal vocabulary (`Director.mode = 'LIVE'`,
`liveVideo`, `?feed=live`, `live_state.py`) is correct and must stay.
"""
from __future__ import annotations

import re
from pathlib import Path

import pytest

REPO = Path(__file__).resolve().parents[4]

#: The words, in the three languages the broadcast renders.
LIVENESS = re.compile(r"\b(live|direkt|直播|replay|repris|回放)\b", re.IGNORECASE)

#: HTML surfaces whose TEXT NODES a viewer reads. `ui-strings.js` has its own test below (and a
#: stricter one in `ui_strings.test.mjs`), because a JS file has no text nodes to scan.
SURFACES = [
    "v3/broadcast/core/web/index.html",
    "v3/broadcast/core/web/overlay.html",
    "v3/broadcast/core/web/boatcam.html",
    "v3/broadcast/core/web/lineup.html",
    "v3/broadcast/core/web/standings.html",
    "v3/ingest/viewer.html",
    "v3/rie/web/regatta_programme.html",
]

#: Legitimate uses that are NOT claims about the picture, each with the reason it stays.
ALLOWED = {
    # "Replay" as a NAVIGATION control on the interactive viewer — the mode where the user scrubs
    # the past, paired with "Now" for following the frontier. It describes what the USER is doing,
    # not what the picture is, and the user really is replaying.
    "viewer_nav_mode": re.compile(r"^Replay$"),
    # A shot that genuinely IS a replay inside a programme — labelling it is honest, not a mode word.
    "replay_shot_label": re.compile(r"REPLAY · "),
}


def _visible_text(path: Path) -> list[tuple[int, str]]:
    """The page's TEXT NODES — what a viewer actually reads.

    Precise on purpose. An earlier version also scanned quoted strings, which swept up CSS class
    names (`mode-live`), element ids and JS identifiers — none of which a viewer ever sees. A guard
    that cries wolf on `class="live"` gets an exemption list, and an exemption list is how the thing
    it guards comes back.

    Attribute values are excluded by construction (they sit inside the tag, not between tags), and
    `<script>`/`<style>` bodies are stripped so code is never mistaken for copy.
    """
    src = path.read_text(encoding="utf-8", errors="ignore")
    src = re.sub(r"<!--.*?-->", "", src, flags=re.S)
    src = re.sub(r"<script\b.*?</script>", lambda m: "\n" * m.group(0).count("\n"), src, flags=re.S | re.I)
    src = re.sub(r"<style\b.*?</style>", lambda m: "\n" * m.group(0).count("\n"), src, flags=re.S | re.I)

    out: list[tuple[int, str]] = []
    for i, line in enumerate(src.splitlines(), start=1):
        for piece in re.findall(r">([^<>]+)<", line):
            text = piece.strip()
            # Skip template holes (`${…}`) and bare punctuation.
            if text and not text.startswith("${") and re.search(r"[A-Za-z一-鿿]", text):
                out.append((i, text))
    return out


@pytest.mark.parametrize("rel", SURFACES)
def test_no_surface_claims_the_picture_is_live(rel):
    path = REPO / rel
    if not path.is_file():
        pytest.skip(f"{rel} not present")
    offenders = []
    for lineno, text in _visible_text(path):
        if not LIVENESS.search(text):
            continue
        if any(rx.search(text) for rx in ALLOWED.values()):
            continue
        offenders.append(f"{rel}:{lineno}  {text[:90]}")
    assert not offenders, (
        "these render a liveness claim to a viewer; the timestamp is the disclosure "
        "(docs/streamed-picture-style-guide.md §8):\n  " + "\n  ".join(offenders)
    )


def test_the_shared_label_table_has_no_mode_words():
    """The strongest form of the rule: the words are GONE from the vocabulary, not just unused.

    A renamed-but-present key invites the badge back the next time someone wants one.
    """
    src = (REPO / "v3/broadcast/core/web/ui-strings.js").read_text(encoding="utf-8")
    for key in ("LIVE:", "REPLAY:"):
        assert key not in src, f"{key} is back in the shared UI table"


def test_the_commentary_banks_keep_the_other_sense_of_live():
    """A guard on the guard.

    "the full right-of-way rules are live" means IN FORCE — a different word entirely, and scrubbing
    it would damage the commentary to satisfy a rule that was never about it. If this ever fails,
    the fix is the scanner, not the phrase.
    """
    banks = (REPO / "v3/scoring/commentary_banks.py").read_text(encoding="utf-8")
    assert "rules are live" in banks
