r"""
Write a numbered commentary transcript (one CSV per language) from the scored race
feed, for easy review of the broadcast script.

Reads demo/data/races/real.json and, for each language, emits a CSV with one row per
commentary line in race-time order:

    #, local_time, t_sec, role, text

  * #          sequential line number (1-based, per language)
  * local_time wall-clock time of the line in the machine's local zone, ISO 8601 with a
               space (e.g. "2026-05-22 15:06:30") — anchored on meta.trackStartUtcMs + t_sec
  * t_sec      raw seconds from the start of the feed timeline (the gun is at t_sec 195)
  * role       Caller  (voice "m" — play-by-play / facts)
               Analyst (voice "f" — tactics / colour)
  * text       the localized commentary line

The role mapping mirrors the engine (see v3/scoring/score_race.py): the male voice is
the Caller (FACTS — countdown, gun, distance/heading, result), the female voice is the
Analyst (TACTICS & ANALYSIS — intro, colour, explanations).

Files are written UTF-8 with a BOM (utf-8-sig) so Excel opens Swedish/Chinese correctly.

  ..\.venv-demo\Scripts\python.exe export\transcript_csv.py            # all langs -> _cap/
  ..\.venv-demo\Scripts\python.exe export\transcript_csv.py --lang sv  # one language
"""
import argparse
import csv
import json
from datetime import datetime
from pathlib import Path

_HERE = Path(__file__).resolve().parent
ROOT = _HERE.parent.parent                                   # v3/broadcast
FEED = ROOT / "demo" / "data" / "races" / "real.json"

ROLE_BY_VOICE = {"m": "Caller", "f": "Analyst"}


def local_time(start_utc_ms, t_sec):
    """Wall-clock time of a line in the machine's local zone, ISO 8601 with a space.

    fromtimestamp() with no tz returns local time, so the offset is the machine's — i.e.
    the user's local time, per the project's presentation convention."""
    return datetime.fromtimestamp(start_utc_ms / 1000 + t_sec).strftime("%Y-%m-%d %H:%M:%S")


def write_transcript(lang, feed, out_dir):
    start_utc_ms = feed["meta"]["trackStartUtcMs"]           # t_sec 0 anchor
    events = sorted(feed["events"], key=lambda e: e["tSec"])
    rows = []
    for e in events:
        text = (e.get("i18n") or {}).get(lang) or (e.get("text") if lang == "en" else "")
        if not text:
            continue
        rows.append((e["tSec"], ROLE_BY_VOICE.get(e.get("voice"), e.get("voice", "")), text))

    out = out_dir / f"transcript_{lang}.csv"
    with open(out, "w", encoding="utf-8-sig", newline="") as f:
        w = csv.writer(f)
        w.writerow(["#", "local_time", "t_sec", "role", "text"])
        for n, (t_sec, role, text) in enumerate(rows, 1):
            w.writerow([n, local_time(start_utc_ms, t_sec), t_sec, role, text])
    return out, len(rows)


def main():
    ap = argparse.ArgumentParser(description="Write numbered commentary transcript CSVs from the race feed.")
    ap.add_argument("--lang", help="single language (en, sv, zh); default: every language present in the feed")
    ap.add_argument("--out", help="output directory (default _cap/)")
    args = ap.parse_args()

    with open(FEED, encoding="utf-8") as f:
        feed = json.load(f)

    langs = [args.lang] if args.lang else sorted(
        set().union(*[set((e.get("i18n") or {}).keys()) for e in feed["events"]]))

    out_dir = Path(args.out) if args.out else ROOT / "_cap"
    out_dir.mkdir(parents=True, exist_ok=True)

    for lang in langs:
        path, n = write_transcript(lang, feed, out_dir)
        print(f"wrote {path}  ({n} lines)")


if __name__ == "__main__":
    main()
