"""wissen_einlesen.py — liest die Texte aus wissen/ in die Tabelle public.wissen.

Jeder Absatz einer .md-Datei wird ein Abschnitt. Für jeden Abschnitt holt das Skript einen
Vektor von einer OpenAI-kompatiblen Schnittstelle (LM Studio, AuxData-Gateway, ...) und
schreibt Text und Vektor über die REST-API von Supabase in die Tabelle.

Nur Python-Standardbibliothek, ab Python 3.9. Einstellungen über Umgebungsvariablen:

  SUPABASE_URL        z. B. http://localhost:8000 (über den SSH-Tunnel)
  SUPABASE_SECRET_KEY der geheime Schlüssel (sb_secret_...), nie in eine Webseite
  EMBED_URL           z. B. http://localhost:1234/v1 (LM Studio)
  EMBED_MODEL         z. B. text-embedding-bge-m3
  EMBED_KEY           API-Schlüssel der Embedding-Schnittstelle (LM Studio: beliebig)

  python wissen_einlesen.py            # liest ein
  python wissen_einlesen.py --leeren   # löscht vorher alle Abschnitte
"""
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path

ORDNER = Path(__file__).resolve().parent / "wissen"
DIMENSIONEN = 1024


def env(name):
    wert = os.environ.get(name, "").strip()
    if not wert:
        sys.exit(f"Umgebungsvariable {name} fehlt (siehe Kopf dieses Skripts).")
    return wert


def anfrage(methode, url, schluessel, daten=None, extra=None):
    kopf = {"Content-Type": "application/json", "Authorization": f"Bearer {schluessel}"}
    kopf.update(extra or {})
    body = json.dumps(daten).encode() if daten is not None else None
    try:
        with urllib.request.urlopen(urllib.request.Request(url, body, kopf, method=methode), timeout=120) as r:
            text = r.read().decode()
            return json.loads(text) if text else None
    except urllib.error.HTTPError as e:
        sys.exit(f"{methode} {url} → HTTP {e.code}: {e.read().decode()[:300]}")
    except urllib.error.URLError as e:
        sys.exit(f"{url} nicht erreichbar ({e.reason}). Läuft der Tunnel bzw. der Server?")


def abschnitte():
    for datei in sorted(ORDNER.glob("*.md")):
        # Windows-Zeilenenden angleichen, sonst findet split keinen einzigen Absatz
        for absatz in datei.read_text(encoding="utf-8").replace("\r\n", "\n").split("\n\n"):
            absatz = absatz.strip()
            if absatz and not absatz.startswith("#"):
                yield datei.name, absatz


def main():
    sys.stdout.reconfigure(encoding="utf-8", errors="replace")
    supabase, geheim = env("SUPABASE_URL").rstrip("/"), env("SUPABASE_SECRET_KEY")
    embed_url, modell = env("EMBED_URL").rstrip("/"), env("EMBED_MODEL")
    embed_key = os.environ.get("EMBED_KEY", "lm-studio")
    supa_kopf = {"apikey": geheim, "Prefer": "return=minimal"}

    teile = list(abschnitte())
    if not teile:
        sys.exit(f"Keine Absätze in {ORDNER} gefunden.")

    antwort = anfrage("POST", f"{embed_url}/embeddings", embed_key,
                      {"model": modell, "input": [t for _, t in teile]})
    vektoren = [d["embedding"] for d in sorted(antwort["data"], key=lambda d: d["index"])]
    if len(vektoren[0]) != DIMENSIONEN:
        sys.exit(f"Das Modell liefert {len(vektoren[0])} Werte, die Tabelle erwartet {DIMENSIONEN}. "
                 "Anderes Modell wählen oder vektor.sql anpassen.")

    if "--leeren" in sys.argv:
        anfrage("DELETE", f"{supabase}/rest/v1/wissen?id=gt.0", geheim, extra=supa_kopf)
        print("Tabelle wissen geleert.")

    zeilen = [{"quelle": q, "inhalt": t, "embedding": v} for (q, t), v in zip(teile, vektoren)]
    anfrage("POST", f"{supabase}/rest/v1/wissen", geheim, zeilen, extra=supa_kopf)
    for q, t in teile:
        print(f"  {q:14} {t[:60]}…")
    print(f"{len(zeilen)} Abschnitte eingelesen ({modell}, {DIMENSIONEN} Werte).")


if __name__ == "__main__":
    main()
