#!/usr/bin/env python3
"""
Transloc Sync-Server
=====================
Sehr einfacher, lokaler HTTP-Server, der den aktuellen Gesprächszustand
zwischen Smartphone 1 (transloc.html - Mikrofon/Steuerung) und
Smartphone 2 (transloc-partner.html - reine Anzeige fuer den
Gespraechspartner) synchronisiert.

Zusaetzlich fungiert er als schmaler Proxy zu Ollama, damit Ollama selbst
NIE direkt oeffentlich erreichbar sein muss (nur /api/chat wird
weitergereicht, keine Verwaltungsfunktionen wie delete/pull/create).

Start:
    python3 sync_server.py [PORT]

Standard-Port: 5115

Endpunkte:
    GET  /state          -> aktueller Gesamtzustand als JSON
    POST /update         -> Teilzustand mergen
    POST /reset          -> Gespraechsverlauf (log) leeren
    POST /translate       -> leitet an Ollama /api/chat weiter (Proxy)
    GET  /ollama-status   -> leitet an Ollama /api/tags weiter (Proxy)
"""
import json
import sys
import threading
import urllib.request
import urllib.error
from urllib.parse import urlsplit, parse_qs
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

DEFAULT_PORT = 5115
MAX_LOG_ENTRIES = 200
OLLAMA_BASE = "http://127.0.0.1:11434"  # nur lokal, nie oeffentlich

APP_KEY = "5473a890ac45f2a774b765b5148733de4ab2cf69"

state_lock = threading.Lock()
state = {
    "version": 0,
    "targetLanguage": "Spanisch",
    "recognizingGerman": True,
    "listening": False,
    "speaking": False,
    "log": [],  # [{role: 'partner'|'you', text: '...'}, ...]
}


class Handler(BaseHTTPRequestHandler):
    server_version = "TranslocSync/1.1"

    def _send_json(self, payload, status=200):
        body = json.dumps(payload).encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()
        self.wfile.write(body)

    def do_OPTIONS(self):
        self._send_json({})

    def _has_valid_key(self):
        query = urlsplit(self.path).query
        supplied = parse_qs(query).get("key", [None])[0]
        return supplied == APP_KEY

    def do_GET(self):
        if not self._has_valid_key():
            self._send_json({"error": "unauthorized"}, status=401)
            return
        if self.path.startswith("/state"):
            with state_lock:
                self._send_json(state)
        elif self.path.startswith("/ollama-status"):
            self._proxy_ollama_get("/api/tags")
        else:
            self._send_json({"error": "not found"}, status=404)

    def do_POST(self):
        if not self._has_valid_key():
            self._send_json({"error": "unauthorized"}, status=401)
            return
        length = int(self.headers.get("Content-Length", 0) or 0)
        raw = self.rfile.read(length) if length else b"{}"
        try:
            data = json.loads(raw.decode("utf-8")) if raw.strip() else {}
        except json.JSONDecodeError:
            self._send_json({"error": "invalid json"}, status=400)
            return

        if self.path.startswith("/update"):
            with state_lock:
                for key in ("targetLanguage", "recognizingGerman", "listening", "speaking"):
                    if key in data:
                        state[key] = data[key]
                entry = data.get("logEntry")
                if isinstance(entry, dict) and entry.get("role") in ("partner", "you") and "text" in entry:
                    state["log"].append({"role": entry["role"], "text": entry["text"]})
                    if len(state["log"]) > MAX_LOG_ENTRIES:
                        state["log"] = state["log"][-MAX_LOG_ENTRIES:]
                state["version"] += 1
                self._send_json(state)
        elif self.path.startswith("/reset"):
            with state_lock:
                state["log"] = []
                state["version"] += 1
                self._send_json(state)
        elif self.path.startswith("/translate"):
            self._proxy_ollama_post("/api/chat", data)
        else:
            self._send_json({"error": "not found"}, status=404)

    def _proxy_ollama_post(self, ollama_path, data):
        # Nur genau die Felder weiterreichen, die die App tatsaechlich braucht -
        # kein direkter Durchgriff auf beliebige Ollama-Parameter von aussen.
        payload = {
            "model": data.get("model"),
            "messages": data.get("messages", []),
            "stream": False,
            "think": bool(data.get("think", False)),
        }
        body = json.dumps(payload).encode("utf-8")
        req = urllib.request.Request(
            OLLAMA_BASE + ollama_path,
            data=body,
            headers={"Content-Type": "application/json"},
            method="POST",
        )
        try:
            with urllib.request.urlopen(req, timeout=120) as resp:
                result = json.loads(resp.read().decode("utf-8"))
            self._send_json(result)
        except urllib.error.URLError as e:
            self._send_json({"error": f"Ollama nicht erreichbar: {e}"}, status=502)

    def _proxy_ollama_get(self, ollama_path):
        req = urllib.request.Request(OLLAMA_BASE + ollama_path, method="GET")
        try:
            with urllib.request.urlopen(req, timeout=10) as resp:
                result = json.loads(resp.read().decode("utf-8"))
            self._send_json(result)
        except urllib.error.URLError as e:
            self._send_json({"error": f"Ollama nicht erreichbar: {e}"}, status=502)

    def log_message(self, fmt, *args):
        pass


def main():
    port = DEFAULT_PORT
    if len(sys.argv) > 1:
        try:
            port = int(sys.argv[1])
        except ValueError:
            print(f"Ungueltiger Port '{sys.argv[1]}', verwende Standard {DEFAULT_PORT}")
    server = ThreadingHTTPServer(("0.0.0.0", port), Handler)
    print(f"Transloc Sync-Server laeuft auf Port {port} (Strg+C zum Beenden)")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()
