"""Translations for the STATIC JavaScript bundle (static/app.js).

app.js is a plain file, not a template, so it cannot call Jinja's `_()`. The
bridge is:

  1. In app.js, every user-visible literal is wrapped in `t('…')`.
  2. pybabel extracts those `t()` calls straight out of the .js (see babel.cfg's
     [javascript:] section), so they land in the same catalog as everything else
     — one translation workflow, not two.
  3. `base.html` emits `window.I18N = {…}` immediately before app.js loads, and
     `t()` looks the string up there, falling back to the English source.

The key list is derived from app.js itself rather than being maintained by hand,
so it cannot drift out of sync with the code.
"""

from __future__ import annotations

import re
from pathlib import Path

_JS_PATH = Path(__file__).resolve().parent / "static" / "app.js"
# t('…') / t("…"), single- or double-quoted, no escaped-quote handling needed
# because the wrapper is only ever applied to plain literals.
_CALL = re.compile(r"""\bt\(\s*(?P<q>['"])(?P<s>(?:(?!(?P=q)).)*)(?P=q)\s*\)""")

_keys: list[str] | None = None


def js_string_keys() -> list[str]:
    """Every string app.js asks to have translated. Scanned once, then cached."""
    global _keys
    if _keys is None:
        try:
            src = _JS_PATH.read_text(encoding="utf-8")
        except OSError:
            _keys = []
        else:
            _keys = sorted({m.group("s") for m in _CALL.finditer(src)})
    return _keys


def js_translations() -> dict[str, str]:
    """{source: translation} for the active locale, for injection into the page."""
    from flask_babel import gettext

    out = {}
    for key in js_string_keys():
        value = gettext(key)
        if value != key:          # only ship what actually differs
            out[key] = value
    return out
