"""Shared pieces: vocabulary scoring and the worker call (deepseek-v4-flash via AI Builder Space)."""
import json, os, re, time, concurrent.futures as cf
from pathlib import Path
import httpx

HERE = Path(__file__).resolve().parent
CHAT = "https://space.ai-builders.com/backend/v1/chat/completions"
WORKER = os.environ.get("WORKER_MODEL", "deepseek-v4-flash")
VOCAB = {
    "zh": ["韩昱", "蔺思远", "思远", "欧阳婧", "仝欣然", "祁骁", "Kaveh", "Priya", "邝敏", "糜乐",
           "Koyeb", "vLLM", "KV cache", "LoRA", "MLX", "AI Builder Space", "Superlinear", "intake", "VoiceFlow", "Codex", "prefill"],
    "en": ["Jaxon", "Saoirse", "Kaitlynn", "Xochitl", "Aoife", "Kaveh", "Priya", "Mairead", "Tadhg",
           "Koyeb", "vLLM", "KV cache", "LoRA", "MLX", "AI Builder Space", "Superlinear", "intake", "VoiceFlow", "Codex", "prefill"],
}
norm = lambda s: s.lower().replace(" ", "")


def score_item(truth: str, hyp: str, lang: str):
    """correct = mentions written right (capped by the script count); wrong_inserts = extra mentions not in the script."""
    ref, out = norm(truth), norm(hyp)
    total = correct = inserts = 0
    misses, extras = {}, {}
    for v in VOCAB[lang]:
        if lang == "en":
            # whole word, case-insensitive; multi-word terms also match with a hyphen or no space ("KV-cache", "KVcache")
            pat = re.compile(r"(?<![A-Za-z0-9])" + r"[\s-]?".join(map(re.escape, v.split())) + r"(?![A-Za-z0-9])", re.I)
            n, k = len(pat.findall(truth)), len(pat.findall(hyp))
        else:
            n, k = ref.count(norm(v)), out.count(norm(v))
        total += n; correct += min(n, k)
        if k < n: misses[v] = n - k
        if k > n: inserts += k - n; extras[v] = k - n
    return {"total": total, "correct": correct, "inserts": inserts, "misses": misses, "extras": extras}


def aggregate(results):
    t = sum(r["total"] for r in results); c = sum(r["correct"] for r in results); i = sum(r["inserts"] for r in results)
    return {"total": t, "correct": c, "inserts": i, "score": round((c - i) / t, 4) if t else None}


def worker(skill: str, names: str, raw: str) -> str:
    body = {"model": WORKER, "temperature": 0, "messages": [
        {"role": "system", "content": skill.strip() + "\n\n" + names.strip()},
        {"role": "user", "content": raw}]}
    for attempt in range(6):
        try:
            r = httpx.post(CHAT, json=body, headers={"Authorization": f"Bearer {os.environ['AI_BUILDER_TOKEN']}"}, timeout=180)
            r.raise_for_status()
            return r.json()["choices"][0]["message"]["content"].strip()
        except Exception as e:
            time.sleep(4 * (attempt + 1)); last = e
    raise RuntimeError(f"worker failed: {last}")


def run_skill(skill, names, items, lang, threads=8):
    with cf.ThreadPoolExecutor(threads) as ex:
        outs = list(ex.map(lambda it: worker(skill, names, it["raw"]), items))
    res = []
    for it, o in zip(items, outs):
        s = score_item(it["truth"], o, lang); s.update(id=it["id"], output=o)
        res.append(s)
    return res
