#!/usr/bin/env python3
"""EM2 claude-bridge — thin LAN HTTP service wrapping headless `claude -p`.

Rides the Claude Code plan (no API keys anywhere). LAN-only by design.
POST /chat  {"system": "...", "prompt": "...", "model": "opus"(optional)}
         -> {"reply": "..."}
GET  /health -> {"ok": true}

Run:  py -3.12 -X utf8 claude_bridge.py     (port 8896)
Or as a service:  nssm install em2-bridge <python> <this file>
"""
import json
import subprocess
import sys
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

PORT = 8896
TIMEOUT_S = 180
CLAUDE = "claude"  # on PATH for the service account


def ask_claude(system: str, prompt: str, model: str | None) -> str:
    cmd = [CLAUDE, "-p", "--output-format", "text"]
    if model:
        cmd += ["--model", model]
    if system:
        cmd += ["--append-system-prompt", system]
    proc = subprocess.run(
        cmd,
        input=prompt,
        capture_output=True,
        text=True,
        encoding="utf-8",
        timeout=TIMEOUT_S,
        shell=True,  # claude is a .cmd shim on Windows
    )
    if proc.returncode != 0:
        detail = (proc.stderr or "").strip() or (proc.stdout or "").strip()
        raise RuntimeError(
            f"claude -p rc={proc.returncode}: {detail[:800]}" if detail
            else f"claude -p rc={proc.returncode} (no output)")
    return proc.stdout.strip()


class Handler(BaseHTTPRequestHandler):
    def _send(self, code: int, obj: dict) -> None:
        body = json.dumps(obj).encode("utf-8")
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def do_OPTIONS(self):  # CORS preflight for the web build
        self._send(200, {"ok": True})

    def do_GET(self):
        if self.path == "/health":
            self._send(200, {"ok": True})
        else:
            self._send(404, {"error": "not found"})

    def do_POST(self):
        if self.path != "/chat":
            self._send(404, {"error": "not found"})
            return
        try:
            length = int(self.headers.get("Content-Length", 0))
            data = json.loads(self.rfile.read(length) or b"{}")
            reply = ask_claude(
                data.get("system", ""),
                data.get("prompt", ""),
                data.get("model") or None,
            )
            self._send(200, {"reply": reply})
        except Exception as exc:  # surface the cause to the app, plainly
            self._send(500, {"error": str(exc)[:500]})

    def log_message(self, fmt, *args):
        print(f"[em2-bridge] {self.address_string()} {fmt % args}",
              file=sys.stderr)


if __name__ == "__main__":
    print(f"[em2-bridge] listening on :{PORT} (LAN only — do not expose)")
    ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
