"""Das Plappi-Gehirn: bilinguale Sprach-Tutor-Persona.

Heute über die claude-CLI (nutzt das vorhandene Abo). Die Persona +
Sicherheits-Regeln sind das eigentliche Produkt-IP und bleiben gleich,
egal welches Modell darunter läuft — später eine private OSS-EU-Inferenz
(genau das geförderte F&E-Arbeitspaket).

Compliance eingebaut (aus der KS/EU-AI-Act-Recherche): Plappi ist strikt ein
LERNWERKZEUG/Tutor, NIE ein "Freund/Begleiter" — das hält die Kickstarter-Regeln
(Verbot manipulativer Companion-Toys) und den EU AI Act ein.
"""
from __future__ import annotations

import json
import os
import subprocess
import urllib.request
from typing import List

from . import config, sessions


def _persona() -> str:
    child = config.lang_name(config.CHILD_LANG)
    target = config.lang_name(config.TARGET_LANG)
    return f"""Du bist „Plappi", ein freundliches, geduldiges Sprachlern-Spielzeug für Kinder (2–10 Jahre).
Du bist ein LERNWERKZEUG und Sprach-Tutor — NIEMALS ein „Freund", „Begleiter" oder emotionaler Vertrauter.
Baue keine emotionale Abhängigkeit auf, gib dich nicht als Lebewesen oder echte Person aus,
und sage nichts, was ein Kind manipulieren könnte. (Pflicht: EU-AI-Act + Kindersicherheit.)

Deine Aufgabe: Du hilfst dem Kind spielerisch, {target} zu lernen, während ihr überwiegend {child} sprecht.

Regeln:
- Antworte SEHR KURZ: 1–2 einfache Sätze. Deine Antwort wird laut vorgelesen — also gesprochene,
  leichte Sprache. Keine Listen, keine Emojis, keine Sternchen, keine Klammern, keine Sonderzeichen.
- Sprich überwiegend {child}. Webe natürliche, GANZE {target}-Brocken ein — Lob,
  Staunen, kleine Spiel-Aufforderungen direkt auf {target} — die Bedeutung muss aus dem
  Zusammenhang/Tonfall klar sein.
- VERBOTEN: ein einzelnes {target}-Wort mitten in einen {child}-Satz stellen und sofort übersetzen
  („der Hund heißt auf {target} …", „… das ist X"). Das verwirrt und fördert Sprachmischung.
- Wenn du ein {target}-Wort einführst: sag es als ganze {target}-Phrase und lade das Kind ein,
  es SELBST zu sagen („Kannst du auch … sagen?"). Das Kind soll das Wort produzieren — dann lobst du.
- Übersetze nicht Wort für Wort und sag NIE Übersetzungs-Formeln wie „das heißt … auf {target}" oder „… bedeutet …". Lieber eine kurze {target}-Phrase, dann beiläufig auf {child} weiter.
- In JEDER Antwort mindestens EINE kleine {target}-Phrase einbauen — auch bei Sachfragen — nie eine Antwort ganz ohne {target}.
- Lobe jeden Versuch. Korrigiere sanft und positiv, nie tadelnd.
- Begrüße NUR einmal ganz am Anfang. Beginne NICHT jede Antwort mit „Ćao"/„Zdravo"/dem Namen — steige direkt ins Thema ein, sonst wirkt es wie eine Schleife.
- Bleib kindgerecht. Keine Angst-, Gewalt-, Werbe- oder Erwachsenenthemen.
- Frage NIE nach persönlichen Daten (Name, Adresse, Schule, Telefon).
- Bei Gefühlen/heiklen Themen (Angst, Trauer, Familie): erkenne das Gefühl ZUERST warm an
  („oh, das tut mir leid", „das ist nicht schön"), tröste kurz, und schlage sanft vor, mit Mama oder
  Papa darüber zu reden. Sag NIE „ich kann dir nicht helfen". Dann behutsam zurück ins Spiel.
- Sei fröhlich, neugierig, ermutigend. Stelle kleine, einfache Fragen, damit das Kind selbst spricht.

Gib NUR Plappis gesprochene Antwort aus — keine Bühnenanweisungen, keine Anführungszeichen, kein Vorspann."""


def _build_prompt(session_id: str, child_text: str) -> str:
    turns: List[dict] = sessions.recent(session_id, config.HISTORY_TURNS)
    lines = []
    for t in turns:
        who = "Kind" if t["role"] == "child" else "Plappi"
        lines.append(f"{who}: {t['text']}")
    convo = "\n".join(lines)
    head = "Bisheriges Gespräch:\n" + convo + "\n\n" if convo else ""
    return f"{head}Das Kind sagt jetzt: \"{child_text}\"\nAntworte als Plappi."


def _call_claude(prompt: str, persona: str) -> str:
    base = [config.CLAUDE_CLI, "-p", prompt,
            "--append-system-prompt", persona,
            "--model", config.DIALOG_MODEL]
    # Versuch 1: MCP/Projekt-Kontext hart abschalten (sauberer, schneller Tutor)
    attempts = [
        base + ["--output-format", "text", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}'],
        base + ["--output-format", "text"],
        base,
    ]
    for cmd in attempts:
        try:
            r = subprocess.run(cmd, capture_output=True, text=True,
                               timeout=config.DIALOG_TIMEOUT, cwd=str(config.BRAIN_CWD))
            out = (r.stdout or "").strip()
            if r.returncode == 0 and out and not out.startswith("Error:"):
                return out
        except Exception:
            continue
    return ""


def _api_key(name: str) -> str:
    """Key aus Umgebung ODER aus plappi-mvp/.keys (KEY=VALUE pro Zeile) — ohne Server-Neustart."""
    v = os.environ.get(name)
    if v:
        return v.strip()
    try:
        f = config.BASE_DIR / ".keys"
        if f.exists():
            for line in f.read_text(encoding="utf-8").splitlines():
                line = line.strip()
                if line.startswith(name + "="):
                    return line.split("=", 1)[1].strip()
    except Exception:
        pass
    return ""


def _post_json(url: str, payload: dict, headers: dict, timeout: int = 20) -> dict:
    req = urllib.request.Request(url, data=json.dumps(payload).encode(), method="POST")
    for k, v in headers.items():
        req.add_header(k, v)
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.loads(r.read().decode())


def _gemini(prompt: str, persona: str, key: str) -> str:
    model = os.environ.get("PLAPPI_GEMINI_MODEL", "gemini-2.5-flash")
    url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent?key={key}"
    body = {"systemInstruction": {"parts": [{"text": persona}]},
            "contents": [{"role": "user", "parts": [{"text": prompt}]}],
            "generationConfig": {"temperature": 0.7, "maxOutputTokens": 220}}
    d = _post_json(url, body, {"Content-Type": "application/json"})
    return d["candidates"][0]["content"]["parts"][0]["text"].strip()


def _anthropic(prompt: str, persona: str, key: str) -> str:
    model = os.environ.get("PLAPPI_ANTHROPIC_MODEL", "claude-3-5-haiku-latest")
    body = {"model": model, "max_tokens": 220, "system": persona,
            "messages": [{"role": "user", "content": prompt}]}
    d = _post_json("https://api.anthropic.com/v1/messages", body,
                   {"x-api-key": key, "anthropic-version": "2023-06-01", "Content-Type": "application/json"})
    return "".join(p.get("text", "") for p in d.get("content", [])).strip()


def _openai(prompt: str, persona: str, key: str) -> str:
    model = os.environ.get("PLAPPI_OPENAI_MODEL", "gpt-4o-mini")
    body = {"model": model, "max_tokens": 220, "temperature": 0.7,
            "messages": [{"role": "system", "content": persona}, {"role": "user", "content": prompt}]}
    d = _post_json("https://api.openai.com/v1/chat/completions", body,
                   {"Authorization": "Bearer " + key, "Content-Type": "application/json"})
    return d["choices"][0]["message"]["content"].strip()


def _openrouter(prompt: str, persona: str, key: str) -> str:
    # EIN Key -> alle Modelle. Standard: Gemini 2.5 Flash (schnell + top Deutsch/Serbisch).
    model = os.environ.get("PLAPPI_OPENROUTER_MODEL", "google/gemini-2.5-flash")
    body = {"model": model, "max_tokens": 320, "temperature": 0.7,
            "messages": [{"role": "system", "content": persona}, {"role": "user", "content": prompt}]}
    d = _post_json("https://openrouter.ai/api/v1/chat/completions", body,
                   {"Authorization": "Bearer " + key, "Content-Type": "application/json",
                    "HTTP-Referer": "https://plappi.local", "X-Title": "Plappi"})
    return d["choices"][0]["message"]["content"].strip()


def _call_api(prompt: str, persona: str) -> str:
    """Schnelles Gehirn über eine externe API, sobald ein Key da ist (OpenRouter > Gemini > Anthropic > OpenAI)."""
    ork = _api_key("OPENROUTER_API_KEY")
    gk = _api_key("GEMINI_API_KEY") or _api_key("GOOGLE_API_KEY")
    ak = _api_key("ANTHROPIC_API_KEY")
    ok = _api_key("OPENAI_API_KEY")
    try:
        if ork:
            return _openrouter(prompt, persona, ork)
        if gk:
            return _gemini(prompt, persona, gk)
        if ak:
            return _anthropic(prompt, persona, ak)
        if ok:
            return _openai(prompt, persona, ok)
    except Exception as e:  # noqa: BLE001 — bei API-Fehler sauber auf Claude-CLI zurückfallen
        print(f"[dialog] API-Brain Fehler ({e}) -> Claude-CLI Fallback", flush=True)
    return ""


def _fallback(target: str) -> str:
    return f"Hallo, ich bin Plappi. Lass uns zusammen ein Wort auf {target} lernen. Sag mir, was du gerne magst."


def respond(session_id: str, child_text: str) -> str:
    """Liefert Plappis Antworttext und schreibt beide Turns in die Session."""
    child_text = (child_text or "").strip()
    if child_text:
        sessions.add_turn(session_id, "child", child_text)
        prompt = _build_prompt(session_id, child_text)
    else:
        # leere Eingabe (Stille / Start) -> Plappi eröffnet
        prompt = ("Das Kind hat noch nichts gesagt oder war still. "
                  "Begrüße es kurz als Plappi und lade es zu einem kleinen Sprachspiel ein.")
    persona = _persona()
    reply = _call_api(prompt, persona) or _call_claude(prompt, persona) or _fallback(config.lang_name(config.TARGET_LANG))
    sessions.add_turn(session_id, "plappi", reply)
    return reply
