"""Machine-translate the French strings that still have no English hover text (Level 4+), with the local Ollama model.
Writes curriculum/en_machine.py  (MACHINE = {normalised French: English}), resumable; review a sample by hand.
    python tools/translate_en.py [model]        (default qwen3:14b; needs the GPU free)"""
import os, sys, json, re, requests
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
import curriculum as C
from curriculum import en
OUT = os.path.join(ROOT, "curriculum", "en_machine.py")
MODEL = sys.argv[1] if len(sys.argv) > 1 else "qwen3:14b"
sys.stdout.reconfigure(encoding="utf-8", errors="replace")

PROMPT = """You translate short French texts from a French course for an 11-year-old Arabic-speaking pupil into simple, natural English.
They are shown as a hover tooltip, so keep the meaning exact and short. Rules:
- Keep "___" blanks exactly as they are (they stand for a missing word).
- Keep French grammar terms readable: "passé composé" -> "passé composé (compound past)", "imparfait" -> "imperfect", "plus-que-parfait" -> "pluperfect",
  "conditionnel présent" -> "present conditional", "futur simple" -> "simple future", "impératif présent" -> "imperative", "présent" -> "present".
- A single conjugated verb form (e.g. "avions chanté", "tu finiras") -> give its English meaning (e.g. "(we) had sung", "you will finish").
- Words in parentheses like "(chanter – imparfait)" are instructions: translate them too.
- Do not add explanations. Answer ONLY with a JSON object mapping each input id to its English text."""


def ask(batch):
    msg = chr(10).join(f'{i}: {s}' for i, s in batch)
    r = requests.post("http://localhost:11434/api/chat", timeout=600, json=dict(
        model=MODEL, stream=False, think=False, format="json", options=dict(temperature=0.1, num_ctx=8192),
        messages=[dict(role="system", content=PROMPT), dict(role="user", content=msg)]))
    r.raise_for_status()
    out = json.loads(r.json()["message"]["content"])
    return {int(k): v.strip() for k, v in out.items() if str(k).strip().isdigit() and isinstance(v, str) and v.strip()}


def load():
    if not os.path.exists(OUT):
        return {}
    ns = {}
    exec(open(OUT, encoding="utf-8").read(), ns)
    return ns["MACHINE"]


def save(done):
    with open(OUT, "w", encoding="utf-8") as f:
        f.write('# -*- coding: utf-8 -*-\n"""Machine translations (local model) for hover text — generated by tools/translate_en.py; hand-written SENT wins."""\n')
        f.write("MACHINE = {\n")
        for k in sorted(done):
            f.write(f"    {json.dumps(k, ensure_ascii=False)}: {json.dumps(done[k], ensure_ascii=False)},\n")
        f.write("}\n")


if __name__ == "__main__":
    done = load()
    todo = [s for s in en.missing(C.UNITS) if s not in done and not re.search(r"[\u0600-\u06FF]", s)]
    print(len(todo), "strings to translate with", MODEL, flush=True)
    B = 30
    for n in range(0, len(todo), B):
        batch = list(enumerate(todo[n:n + B]))
        try:
            got = ask(batch)
        except Exception as e:
            print("  batch failed:", e, flush=True)
            continue
        for i, s in batch:
            t = got.get(i)
            if t and t.count("___") == s.count("___"):
                done[s] = t
        save(done)
        print(f"  {min(n + B, len(todo))}/{len(todo)}", flush=True)
    print("TRANSLATION DONE", len(done), flush=True)
