"""Extract a manifest's English annotation strings and merge authored
translations back into each clip's `strings` dict (no schema change).

Workflow:
  1. `extract` writes tools/i18n/catalogs/en.json — the English strings.
  2. A translator (human or LLM) authors es.json / fr.json / ja.json with the
     SAME structure (lists stay lists of the same length).
  3. `merge` validates structure and writes strings[lang] into the manifest.
"""
from __future__ import annotations

import argparse
import json
from pathlib import Path


def extract_en_catalog(manifest: dict) -> dict:
    out = {}
    for clip in manifest.get("clips", []):
        en = (clip.get("strings") or {}).get("en")
        if en:
            out[clip["id"]] = en
    return out


def _check_shape(key: str, en_val, tr_val):
    if isinstance(en_val, list):
        if not isinstance(tr_val, list) or len(tr_val) != len(en_val):
            raise ValueError(f"{key}: expected list of {len(en_val)} tiers, got {tr_val!r}")
    elif not isinstance(tr_val, str):
        raise ValueError(f"{key}: expected string, got {tr_val!r}")


def merge_catalog(manifest: dict, lang: str, catalog: dict) -> dict:
    if lang == "en":
        raise ValueError("refusing to overwrite the en source")
    for clip in manifest.get("clips", []):
        en = (clip.get("strings") or {}).get("en")
        tr = catalog.get(clip["id"])
        if not en or not tr:
            continue
        for key, en_val in en.items():
            if key not in tr:
                raise ValueError(f"{clip['id']}/{key}: missing in {lang} catalog")
            _check_shape(f"{clip['id']}/{key}", en_val, tr[key])
        clip["strings"][lang] = {k: tr[k] for k in en}  # en key order, lang values
    return manifest


def _main(argv=None):
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("command", choices=["extract", "merge"])
    ap.add_argument("--manifest", default="simulator/sample_media/manifest.json")
    ap.add_argument("--catalog-dir", default="tools/i18n/catalogs")
    ap.add_argument("--lang", help="merge: target language code")
    args = ap.parse_args(argv)

    mpath = Path(args.manifest)
    manifest = json.loads(mpath.read_text(encoding="utf-8"))
    cdir = Path(args.catalog_dir)

    if args.command == "extract":
        cdir.mkdir(parents=True, exist_ok=True)
        (cdir / "en.json").write_text(
            json.dumps(extract_en_catalog(manifest), ensure_ascii=False, indent=1) + "\n",
            encoding="utf-8",
        )
        print(f"wrote {cdir / 'en.json'}")
    else:
        if not args.lang:
            ap.error("merge requires --lang")
        catalog = json.loads((cdir / f"{args.lang}.json").read_text(encoding="utf-8"))
        merge_catalog(manifest, args.lang, catalog)
        mpath.write_text(json.dumps(manifest, ensure_ascii=False, indent=1) + "\n", encoding="utf-8")
        print(f"merged {args.lang} into {mpath}")


if __name__ == "__main__":
    _main()
