#!/usr/bin/env python3
"""Goob's home server: serves the site and gives Goob a mind at /api/goob.

Run from this folder:   python3 goob_server.py
Then open:              http://localhost:8090

Goob's mind, pick one (set before starting):
  Ollama on your PC (free):  export OLLAMA_URL=http://<your-pc-ip>:11434  OLLAMA_MODEL=llama3.1:8b
  Claude API (paid):         export ANTHROPIC_API_KEY=sk-ant-...         CLAUDE_MODEL=claude-haiku-5-5
With neither set, /api/goob answers 503 and the page falls back to simple replies.

If the site lives somewhere else (e.g. Namecheap hosting), list it in ALLOW_ORIGIN
(default: https://goob.chaboya.net,http://goob.chaboya.net) and put this server's public
address in goob-config.json next to the site's index.html.
"""
import json, os, time, urllib.request
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer

PORT = int(os.environ.get("PORT", "8090"))
HOST = os.environ.get("HOST", "0.0.0.0")
OLLAMA_URL = os.environ.get("OLLAMA_URL", "").rstrip("/")
OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3.1:8b")
API_KEY = os.environ.get("ANTHROPIC_API_KEY", "")
CLAUDE_MODEL = os.environ.get("CLAUDE_MODEL", "claude-haiku-5-5")
ALLOW = [o.strip() for o in os.environ.get("ALLOW_ORIGIN", "https://goob.chaboya.net,http://goob.chaboya.net").split(",") if o.strip()]
LIMIT, WINDOW = 30, 600          # at most 30 messages per visitor every 10 minutes
seen = {}

def think(system, messages):
    messages = [m for m in messages if m.get("role") in ("user", "assistant") and m.get("content")][-10:]
    if API_KEY:
        body = {"model": CLAUDE_MODEL, "max_tokens": 220, "system": system, "messages": messages}
        req = urllib.request.Request("https://api.anthropic.com/v1/messages", json.dumps(body).encode(),
                                     {"content-type": "application/json", "x-api-key": API_KEY, "anthropic-version": "2023-06-01"})
        with urllib.request.urlopen(req, timeout=60) as r:
            return "".join(b.get("text", "") for b in json.load(r)["content"])
    if OLLAMA_URL:
        body = {"model": OLLAMA_MODEL, "stream": False, "options": {"num_predict": 220},
                "messages": [{"role": "system", "content": system}] + messages}
        req = urllib.request.Request(OLLAMA_URL + "/api/chat", json.dumps(body).encode(), {"content-type": "application/json"})
        with urllib.request.urlopen(req, timeout=120) as r:
            return json.load(r)["message"]["content"]
    return None

class Handler(SimpleHTTPRequestHandler):
    def cors(self):
        origin = self.headers.get("origin", "")
        if origin in ALLOW or "*" in ALLOW:
            self.send_header("access-control-allow-origin", origin or "*")
            self.send_header("access-control-allow-methods", "POST, OPTIONS")
            self.send_header("access-control-allow-headers", "content-type")
            self.send_header("vary", "origin")

    def do_OPTIONS(self):
        self.send_response(204); self.cors(); self.send_header("content-length", "0"); self.end_headers()

    def do_POST(self):
        if self.path.rstrip("/") != "/api/goob":
            return self.send_error(404)
        try:
            data = json.loads(self.rfile.read(min(int(self.headers.get("content-length", 0)), 64000)) or b"{}")
        except ValueError:
            return self.send_error(400)
        if data.get("ping"):
            return self.reply(200 if (API_KEY or OLLAMA_URL) else 503, {"ok": bool(API_KEY or OLLAMA_URL)})
        ip = self.headers.get("cf-connecting-ip") or self.client_address[0]
        now = time.time(); hits = [t for t in seen.get(ip, []) if now - t < WINDOW]
        if len(hits) >= LIMIT:
            return self.reply(429, {"error": "goob needs a little break"})
        seen[ip] = hits + [now]
        try:
            text = think(str(data.get("system", ""))[:12000], data.get("messages", []))
        except Exception as e:
            return self.reply(502, {"error": str(e)[:200]})
        if not text:
            return self.reply(503, {"error": "no mind configured"})
        self.reply(200, {"text": text.strip()})

    def reply(self, code, obj):
        out = json.dumps(obj).encode()
        self.send_response(code); self.cors(); self.send_header("content-type", "application/json")
        self.send_header("content-length", str(len(out))); self.end_headers(); self.wfile.write(out)

if __name__ == "__main__":
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    mind = "Claude API" if API_KEY else ("Ollama at " + OLLAMA_URL) if OLLAMA_URL else "simple replies only"
    print(f"Goob's brain on http://{HOST}:{PORT}  (mind: {mind})")
    ThreadingHTTPServer((HOST, PORT), Handler).serve_forever()
