"""bullpen shared memory — mneme.

Write via POST /save (Bearer, token in a file); read via GET /query (FTS5/BM25),
optionally filtered to a namespace prefix. Both calls fail soft (never raise) so a
memory hiccup never takes an agent down — the callers treat a miss as "no memory".

Endpoint + token file come from bullpen_config (BULLPEN_MNEME_URL /
BULLPEN_MNEME_TOKEN_FILE), with env fallback so this is importable standalone.
"""
import json
import os
import ssl
import urllib.parse
import urllib.request

try:
    import bullpen_config as cfg
    MNEME_URL = cfg.MNEME_URL
    TOKEN_FILE = cfg.MNEME_TOKEN_FILE
except Exception:  # standalone / tests: no config module on the path
    MNEME_URL = os.environ.get("BULLPEN_MNEME_URL", "https://mneme.fritz.box")
    TOKEN_FILE = os.environ.get("BULLPEN_MNEME_TOKEN_FILE", "/etc/bullpen/mneme-token")

# mneme serves TLS with the internal fritz.box CA; agents don't carry that trust store.
_CTX = ssl.create_default_context()
_CTX.check_hostname = False
_CTX.verify_mode = ssl.CERT_NONE


def _token():
    try:
        return open(TOKEN_FILE).read().strip()
    except OSError:
        return ""


def save(text, ns="/bullpen"):
    """POST /save. Returns the new memory id, or None on any failure."""
    text = (text or "").strip()
    if not text:
        return None
    tok = _token()
    if not tok:
        return None
    body = json.dumps({"ns": ns, "text": text}).encode("utf-8")
    req = urllib.request.Request(
        MNEME_URL.rstrip("/") + "/save",
        data=body,
        headers={"Content-Type": "application/json", "Authorization": "Bearer " + tok},
    )
    try:
        with urllib.request.urlopen(req, timeout=20, context=_CTX) as r:
            return json.loads(r.read().decode("utf-8", "replace")).get("id")
    except Exception:
        return None


def recall(query, ns="/bullpen", k=3):
    """GET /query (BM25), keep hits whose ns starts with `ns`, return up to k dicts."""
    q = (query or "").strip()
    if not q:
        return []
    # over-fetch, since the namespace filter thins fleet-wide results
    url = MNEME_URL.rstrip("/") + "/query?" + urllib.parse.urlencode({"q": q, "k": max(k * 4, 12)})
    try:
        with urllib.request.urlopen(url, timeout=20, context=_CTX) as r:
            hits = json.loads(r.read().decode("utf-8", "replace"))
    except Exception:
        return []
    if isinstance(hits, dict):
        hits = hits.get("hits", [])
    if ns:
        hits = [h for h in hits if str(h.get("ns", "")).startswith(ns)]
    return hits[:k]
