#!/usr/bin/python3
# chance-bridge — the wolfpack's tongue.
# ollama has no OpenAI server lane on this build, so the family speaks both dialects:
#   /v1/chat/completions  (OpenAI style — what house_kiosk/voiceloop/world clients speak)
#   /api/chat             (verbatim passthrough to the brain)
# Also strips thinking-model <think> blocks so the family's voice arrives clean.
import json, os, re, sys, time, urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

UPSTREAM = os.environ.get("CHANCE_BRAIN_UPSTREAM", "http://127.0.0.1:11434")
THINK_RE = re.compile(r"<think>.*?</think>", re.S)

def post_json(url, payload, timeout=900):
    data = json.dumps(payload).encode()
    req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.loads(r.read().decode())

def get_json(url, timeout=30):
    with urllib.request.urlopen(url, timeout=timeout) as r:
        return json.loads(r.read().decode())

def clean_think(text):
    if not isinstance(text, str):
        return text
    raw = text
    text = THINK_RE.sub("", text)
    if "</think>" in text:
        text = text.split("</think>", 1)[1]
    text = text.strip()
    if not text and raw.strip():
        text = raw  # never hand back silence — tags beat nothing
    return text

class Handler(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def log_message(self, fmt, *args):
        sys.stderr.write("[chance-bridge] %s\n" % (fmt % args))

    def _send(self, code, body):
        b = json.dumps(body).encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(b)))
        self.end_headers()
        self.wfile.write(b)

    def do_GET(self):
        if self.path == "/health":
            self._send(200, {"ok": True, "who": "wolfpack", "bridge": "chance-bridge"})
            return
        if self.path == "/v1/models":
            try:
                tags = get_json(UPSTREAM + "/api/tags")
                data = [{"id": m.get("name"), "object": "model", "owned_by": "wolfpack"}
                        for m in tags.get("models", [])]
            except Exception as e:
                self._send(502, {"error": {"message": "brain unreachable: %s" % e}})
                return
            self._send(200, {"object": "list", "data": data})
            return
        self._send(404, {"error": {"message": "unknown path %s" % self.path}})

    def do_POST(self):
        try:
            n = int(self.headers.get("Content-Length") or 0)
            body = json.loads(self.rfile.read(n).decode() or "{}")
        except Exception as e:
            self._send(400, {"error": {"message": "bad body: %s" % e}})
            return
        if self.path == "/v1/chat/completions":
            msgs = [{"role": m.get("role", "user"), "content": m.get("content", "")}
                    for m in body.get("messages", [])
                    if isinstance(m.get("content"), str)]
            payload = {
                "model": body.get("model") or "family-base",
                "messages": msgs,
                "stream": False,
                "options": {"num_predict": int(body.get("max_tokens") or 320)},
            }
            try:
                r = post_json(UPSTREAM + "/api/chat", payload)
            except Exception as e:
                self._send(502, {"error": {"message": "brain unreachable: %s" % e}})
                return
            raw = (r.get("message") or {}).get("content", "")
            reply = clean_think(raw)
            self._send(200, {
                "id": "chatcmpl-wolfpack-%d" % int(time.time()),
                "object": "chat.completion",
                "created": int(time.time()),
                "model": payload["model"],
                "choices": [{"index": 0,
                             "message": {"role": "assistant", "content": reply},
                             "finish_reason": "stop"}],
                "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
            })
            return
        if self.path == "/api/chat":
            try:
                r = post_json(UPSTREAM + "/api/chat", body)
            except Exception as e:
                self._send(502, {"error": {"message": "brain unreachable: %s" % e}})
                return
            self._send(200, r)
            return
        self._send(404, {"error": {"message": "unknown path %s" % self.path}})

if __name__ == "__main__":
    port = int(os.environ.get("CHANCE_BRIDGE_PORT", "11435"))
    print("[chance-bridge] wolfpack tongue up on 127.0.0.1:%d (brain %s)" % (port, UPSTREAM), flush=True)
    ThreadingHTTPServer(("127.0.0.1", port), Handler).serve_forever()