#!/usr/bin/env python3
"""
wrap_textbook_xp.py — Add `data-gamified` attribute to XP UI in textbooks.

Part of Phase 1 of the gamification kill-switch (Variant 2 of plan §E.3).
The CSS rule `body.no-gamification [data-gamified] { display:none }`
catches every wrapped block in one selector, so future authors don't have
to invent new class names just to be hidden by the master switch.

What we wrap:
  • <div id="hero-xp-badge" ...>           — chapter & hub hero badges
  • <div class="po-xp" ...>                 — hub overall-progress XP pill
  • <div class="xp-card" ...>               — chapter inline progress card
  • inline JS that generates `<div class="xp-card">` HTML strings
  • `.xp-bar` containers when they're standalone

Idempotent: if `data-gamified` is already present on an opening tag the
script skips it. Reports each file touched.

Run from repo root:
    python backend/scripts/wrap_textbook_xp.py
"""
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2] / "frontend" / "textbooks"
if not ROOT.is_dir():
    print(f"[wrap-xp] textbook dir not found: {ROOT}", file=sys.stderr)
    sys.exit(1)

# Selectors that mark an opening tag as an XP block. The first group
# captures `<tag` + attributes UP TO (but not including) the existing
# `class="..."` / `id="..."` we matched, so we can splice in
# `data-gamified` before the closing `>`.
PATTERNS = [
    # <... id="hero-xp-badge" ...>
    re.compile(r'(<[a-zA-Z][^>]*?\bid="hero-xp-badge"[^>]*?)(>)'),
    # <... class="po-xp" ...> or class="...po-xp..."
    re.compile(r'(<[a-zA-Z][^>]*?\bclass="[^"]*\bpo-xp\b[^"]*"[^>]*?)(>)'),
    # <... class="...xp-card..."> — covers HTML and inline JS `'<div class="xp-card">'`
    re.compile(r'(<[a-zA-Z][^>]*?\bclass="[^"]*\bxp-card\b[^"]*"[^>]*?)(>)'),
    # Standalone class="hero-xp-badge"
    re.compile(r'(<[a-zA-Z][^>]*?\bclass="[^"]*\bhero-xp-badge\b[^"]*"[^>]*?)(>)'),
]

# Already-wrapped check
RX_HAS_ATTR = re.compile(r'\bdata-gamified\b')


def patch_file(path: Path) -> int:
    """Return number of opening tags newly given data-gamified."""
    text = path.read_text(encoding="utf-8")
    new = text
    changes = 0
    for rx in PATTERNS:
        def sub(m, _rx=rx):
            nonlocal changes
            head, close = m.group(1), m.group(2)
            if RX_HAS_ATTR.search(head):
                return m.group(0)
            changes += 1
            sep = "" if head.endswith(" ") else " "
            return f'{head}{sep}data-gamified{close}'
        new = rx.sub(sub, new)
    if new != text:
        path.write_text(new, encoding="utf-8")
    return changes


def main():
    files = sorted(ROOT.glob("*.html"))
    total_files = 0
    total_tags = 0
    for f in files:
        try:
            n = patch_file(f)
        except Exception as e:
            print(f"[wrap-xp] {f.name}: ERROR {e}", file=sys.stderr)
            continue
        if n:
            print(f"[wrap-xp] {f.name}: +{n} tag(s)")
            total_files += 1
            total_tags += n
    print(f"[wrap-xp] done — patched {total_tags} tags across {total_files} files "
          f"(scanned {len(files)})")


if __name__ == "__main__":
    main()
