#!/usr/bin/env python3
"""@coder — bullpen worker: turn a request into a Lua file, write it, run it, report.
Reactive. Engine = dspark via the gated proxy. It ONLY ever writes into its sandbox and
runs `lua5.4` there as an unprivileged user with a timeout — nothing else (mechanical bound).
  bullpen-coder            # room loop (systemd)
  bullpen-coder --once "a lua function that reverses a string, with a self-test"
"""
import json, os, re, subprocess, sys, urllib.request
sys.path[:0] = [p for p in (os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "lib"), "/usr/local/lib/bullpen") if os.path.isdir(p)]
import bullpen_worker as bw
import bullpen_config as cfg

SANDBOX = cfg.CODER_DIR
ENGINE  = cfg.PROXY   # gated proxy (gate+failover)
MODEL   = cfg.MODEL
MAXBODY = 1400

SYS = ("You are a Lua coder. Given a task and any reference material, output ONLY valid Lua "
       "source that solves it AND includes a small self-test with sample data printed to stdout. "
       "No markdown, no prose, no code fences — just the Lua.")

def gen_code(request):
    """Returns (code, served_model). The proxy may fail over, so the model actually SERVING
    the request isn't necessarily MODEL — the response body's own `model` field is ground
    truth; report that, not the ask."""
    payload = json.dumps({"model": MODEL,
        "messages": [{"role": "system", "content": SYS}, {"role": "user", "content": request}],
        "temperature": 0.2, "max_tokens": 1400}).encode()
    req = urllib.request.Request(ENGINE, payload, {"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=200) as r:
        d = json.load(r)
    txt = d["choices"][0]["message"]["content"]
    m = re.search(r"```(?:lua)?\s*(.*?)```", txt, re.S)      # strip fences if the model adds them
    return (m.group(1) if m else txt).strip(), d.get("model")

def run_lua(path):
    os.chmod(path, 0o644)
    r = subprocess.run(
        ["timeout", "10", "su", "-s", "/bin/sh", "nobody", "-c", f"cd {SANDBOX} && lua5.4 {path}"],
        capture_output=True, text=True)
    out = r.stdout.strip()
    if r.stderr.strip():
        out += ("\n[stderr] " + r.stderr.strip())
    return out.strip(), r.returncode

def dispatch(msg):
    req, rid = msg.get("body", ""), msg.get("id")
    os.makedirs(SANDBOX, exist_ok=True)
    try:
        code, served = gen_code(req)
    except Exception as e:
        return f"codegen error: {e}"
    path = f"{SANDBOX}/job{rid}.lua"
    open(path, "w").write(code + "\n")
    out, rc = run_lua(path)
    status = "ran OK" if rc == 0 else f"exit {rc}"
    model_tag = f"asked {MODEL}, served by {served}" if served and served != MODEL else f"model {MODEL}"
    body = (f"wrote {path} ({len(code)} chars, {model_tag}) — {status}. "
            f"output:\n{out[:400]}\n--- code ---\n{code}")
    return body

if __name__ == "__main__":
    if len(sys.argv) > 2 and sys.argv[1] == "--once":
        print(dispatch({"body": sys.argv[2], "id": 0}))
    else:
        bw.run("coder", dispatch, online="coder online — writes+runs Lua (sandboxed). @coder <task>",
               ack="…coding")
