"""FastMCP 3.x Server für die Gamma.app API."""

from __future__ import annotations

import asyncio
import json
import logging
from contextlib import asynccontextmanager
from typing import Literal

from fastmcp import Context, FastMCP
from pydantic import BaseModel, Field

from gamma31.gamma_client import GammaAPIError, GammaClient

logger = logging.getLogger("gamma31")

# ------------------------------------------------------------------ #
# Static option catalogues (returned by praesentation_vorbereiten)
# ------------------------------------------------------------------ #

_FORMATE = [
    {"wert": "presentation", "beschreibung": "Präsentation (Folien)"},
    {"wert": "document", "beschreibung": "Dokument (scrollbar)"},
    {"wert": "social", "beschreibung": "Social-Media-Post"},
    {"wert": "webpage", "beschreibung": "Webseite"},
]

_TEXTMENGEN = [
    {"wert": "brief", "beschreibung": "Kurz — wenig Text pro Karte"},
    {"wert": "medium", "beschreibung": "Mittel — ausgewogen (Standard)"},
    {"wert": "detailed", "beschreibung": "Ausführlich — viel Text"},
    {"wert": "extensive", "beschreibung": "Sehr ausführlich — maximaler Text"},
]

_TOENE_BEISPIELE = [
    "professionell", "locker", "inspirierend", "akademisch",
    "humorvoll", "sachlich", "motivierend", "formell",
]

_ZIELGRUPPEN_BEISPIELE = [
    "Geschäftsführer", "Investoren", "Studenten", "Kunden",
    "Mitarbeiter", "Fachpublikum", "Allgemeine Öffentlichkeit",
]

_SPRACHEN_BEISPIELE = [
    {"code": "de", "name": "Deutsch"},
    {"code": "en", "name": "Englisch"},
    {"code": "fr", "name": "Französisch"},
    {"code": "es", "name": "Spanisch"},
    {"code": "it", "name": "Italienisch"},
    {"code": "pt", "name": "Portugiesisch"},
    {"code": "nl", "name": "Niederländisch"},
    {"code": "ja", "name": "Japanisch"},
    {"code": "zh", "name": "Chinesisch"},
]

_BILD_QUELLEN = [
    {"wert": "aiGenerated", "beschreibung": "KI-generierte Bilder"},
    {"wert": "unsplash", "beschreibung": "Unsplash-Stockfotos"},
    {"wert": "webImages", "beschreibung": "Web-Bilder"},
    {"wert": "pictographic", "beschreibung": "Piktogramme / Icons"},
    {"wert": "gifs", "beschreibung": "Animierte GIFs"},
    {"wert": "placeholder", "beschreibung": "Platzhalter"},
    {"wert": "noImages", "beschreibung": "Keine Bilder"},
]

_KARTEN_DIMENSIONEN = [
    {"wert": "fluid", "beschreibung": "Flexibel (Standard)"},
    {"wert": "16x9", "beschreibung": "16:9 (Breitbild)"},
    {"wert": "4x3", "beschreibung": "4:3 (Klassisch)"},
    {"wert": "pageless", "beschreibung": "Ohne Seitenbegrenzung"},
    {"wert": "letter", "beschreibung": "US Letter"},
    {"wert": "a4", "beschreibung": "DIN A4"},
]


# ------------------------------------------------------------------ #
# Lifespan
# ------------------------------------------------------------------ #

@asynccontextmanager
async def lifespan(server):  # noqa: ARG001
    """Gamma API Client beim Start initialisieren, beim Shutdown schließen."""
    import os

    client = GammaClient()
    ctx = {"gamma_client": client}

    if not os.environ.get("GAMMA_API_KEY"):
        ctx["api_key_missing"] = True

    try:
        yield ctx
    finally:
        await client.close()


mcp = FastMCP(
    "Gamma Präsentations-Server",
    instructions="""\
Du bist ein Assistent für Gamma.app — du erstellst Präsentationen, Dokumente, \
Social Posts und Webseiten mit KI.

WORKFLOW — Halte diese Reihenfolge ein:

1. VORBEREITEN: Rufe zuerst praesentation_vorbereiten auf. Du erhältst alle \
verfügbaren Optionen (Formate, Textmengen, Tonalitäten, Designs, Ordner). \
Frage den Nutzer dann nach Thema, Format, Zielgruppe, Ton, Sprache, Textmenge \
und Design. Biete konkrete Vorschläge aus den erhaltenen Optionen an.

2. DESIGN SUCHEN (optional): Will der Nutzer ein bestimmtes Design, nutze \
designs_durchsuchen mit einem Suchbegriff, um passende Designs zu finden.

3. ERSTELLEN: Rufe praesentation_erstellen mit allen gesammelten Parametern auf. \
Das Tool wartet automatisch auf Fertigstellung und gibt die URL direkt zurück.

4. ERGEBNIS: Präsentiere dem Nutzer die fertige URL und ggf. Download-Links.

Fasse dich bei Rückfragen kurz. Biete sinnvolle Vorschläge an. \
Wenn der Nutzer schnell loslegen will, verwende vernünftige Defaults \
(Deutsch, professionell, mittel) und frag nur das Nötigste.""",
    lifespan=lifespan,
)


# ------------------------------------------------------------------ #
# Helpers
# ------------------------------------------------------------------ #

def _get_client(ctx: Context) -> GammaClient:
    if ctx.lifespan_context.get("api_key_missing"):
        raise GammaAPIError(
            401,
            "GAMMA_API_KEY ist nicht gesetzt. "
            "Bitte als Umgebungsvariable konfigurieren: "
            "GAMMA_API_KEY=sk-gamma-xxxxxxxx",
        )
    return ctx.lifespan_context["gamma_client"]


def _error_response(message: str) -> str:
    return json.dumps(
        {"fehler": True, "nachricht": message},
        ensure_ascii=False,
        indent=2,
    )


def _success_response(data: dict) -> str:
    return json.dumps(data, ensure_ascii=False, indent=2)


def _simplify_items(items: list[dict], keys: tuple[str, ...] = ("id", "name")) -> list[dict]:
    """Strip API response items down to only the requested keys."""
    return [{k: item[k] for k in keys if k in item} for item in items]


async def _poll_until_done(
    client: GammaClient,
    ctx: Context,
    generation_id: str,
    max_wait: int = 300,
) -> dict:
    """Poll a generation until completed/failed/timeout, with progress reporting."""
    elapsed = 0
    result: dict = {}
    while elapsed < max_wait:
        result = await client.get_generation(generation_id)
        status = result.get("status", "unknown")

        await ctx.report_progress(
            progress=elapsed,
            total=max_wait,
            message=f"Gamma-Status: {status}",
        )

        if status in ("completed", "failed"):
            break

        await asyncio.sleep(5)
        elapsed += 5
    else:
        return {
            "generationId": generation_id,
            "status": "timeout",
            "_hinweis": (
                f"Kein Ergebnis nach {max_wait} Sekunden. "
                "Versuche ergebnis_abrufen erneut."
            ),
        }

    if result.get("credits"):
        total = await ctx.get_state("gamma_credits_verbraucht") or 0
        deducted = result["credits"].get("deducted", 0)
        new_total = total + deducted
        await ctx.set_state("gamma_credits_verbraucht", new_total)
        result["_session_credits_gesamt"] = new_total

    return result


# ------------------------------------------------------------------ #
# Elicitation (Sicherheitsnetz — falls der Client es unterstützt)
# ------------------------------------------------------------------ #

class GenerierungsOptionen(BaseModel):
    """Optionale Einstellungen vor der Gamma-Generierung."""

    sprache: str = Field(
        default="",
        description="Sprachcode (z.B. de, en, fr, es). Leer = Deutsch.",
    )
    zielgruppe: str = Field(
        default="",
        description="Für wen? z.B. Geschäftsführer, Studenten, Kunden",
    )
    tonalitaet: str = Field(
        default="",
        description="Stil, z.B. professionell, locker, inspirierend",
    )
    textmenge: Literal["brief", "medium", "detailed", "extensive"] = Field(
        default="medium",
        description="Wie viel Text pro Folie/Karte",
    )
    zusaetzliche_anweisungen: str = Field(
        default="",
        description="Weitere Anweisungen an die KI (optional)",
    )


async def _try_elicit_prefs(
    ctx: Context,
    text_sprache: str | None,
    text_zielgruppe: str | None,
    text_ton: str | None,
    text_menge: str | None,
    zusaetzliche_anweisungen: str | None,
) -> tuple[str | None, str | None, str | None, str | None, str | None]:
    """Versucht, den Nutzer per ctx.elicit() nach Präferenzen zu fragen.

    Gibt die (ggf. ergänzten) Werte zurück. Wenn Elicitation nicht
    unterstützt wird oder fehlschlägt, werden die Eingabewerte unverändert
    zurückgegeben.
    """
    try:
        logger.info("Versuche ctx.elicit() für Generierungsoptionen …")
        result = await ctx.elicit(
            "Bevor die Gamma erstellt wird — ein paar optionale Angaben "
            "für ein besseres Ergebnis (einfach bestätigen für Standardwerte):",
            response_type=GenerierungsOptionen,
        )
        logger.info("ctx.elicit() Ergebnis: action=%s, data=%s", result.action, result.data)

        if result.action == "accept" and result.data:
            opts = result.data
            if not text_sprache and opts.sprache:
                text_sprache = opts.sprache
            if not text_zielgruppe and opts.zielgruppe:
                text_zielgruppe = opts.zielgruppe
            if not text_ton and opts.tonalitaet:
                text_ton = opts.tonalitaet
            if not text_menge and opts.textmenge:
                text_menge = opts.textmenge
            if not zusaetzliche_anweisungen and opts.zusaetzliche_anweisungen:
                zusaetzliche_anweisungen = opts.zusaetzliche_anweisungen
            logger.info("Elicitation akzeptiert — Werte übernommen.")
        elif result.action == "decline":
            logger.info("Nutzer hat Elicitation abgelehnt — fahre mit Eingabewerten fort.")
        elif result.action == "cancel":
            logger.info("Nutzer hat abgebrochen.")
    except Exception as exc:
        logger.warning("ctx.elicit() nicht unterstützt oder fehlgeschlagen: %s: %s", type(exc).__name__, exc)

    return text_sprache, text_zielgruppe, text_ton, text_menge, zusaetzliche_anweisungen


# ------------------------------------------------------------------ #
# Tool 1: praesentation_vorbereiten (NEU — Informations-Tool)
# ------------------------------------------------------------------ #

@mcp.tool(tags={"workflow"}, timeout=15.0)
async def praesentation_vorbereiten(ctx: Context) -> str:
    """Rufe dieses Tool ZUERST auf, bevor du eine Präsentation erstellst.

    Gibt alle verfügbaren Optionen zurück: Formate, Textmengen, Tonalitäten,
    Zielgruppen, Sprachen, beliebte Designs und Ordner. Nutze diese Infos,
    um dem Nutzer konkrete Vorschläge zu machen, bevor du
    praesentation_erstellen aufrufst.
    """
    try:
        client = _get_client(ctx)

        themes_task = asyncio.create_task(client.list_themes(limit=10))
        folders_task = asyncio.create_task(client.list_folders(limit=20))

        themes_raw, folders_raw = await asyncio.gather(
            themes_task, folders_task, return_exceptions=True,
        )

        top_themes: list[dict] = []
        if isinstance(themes_raw, dict):
            top_themes = _simplify_items(themes_raw.get("data", []))
        else:
            logger.error("Themes laden fehlgeschlagen: %s", themes_raw)

        ordner: list[dict] = []
        if isinstance(folders_raw, dict):
            ordner = _simplify_items(folders_raw.get("data", []))
        else:
            logger.error("Ordner laden fehlgeschlagen: %s", folders_raw)

        optionen = {
            "formate": _FORMATE,
            "textmengen": _TEXTMENGEN,
            "toene_beispiele": _TOENE_BEISPIELE,
            "zielgruppen_beispiele": _ZIELGRUPPEN_BEISPIELE,
            "sprachen_beispiele": _SPRACHEN_BEISPIELE,
            "bild_quellen": _BILD_QUELLEN,
            "karten_dimensionen": _KARTEN_DIMENSIONEN,
            "top_designs": top_themes,
            "ordner": ordner,
            "_hinweis": (
                "Nutze designs_durchsuchen mit einem Suchbegriff, "
                "um nach weiteren Designs zu suchen."
            ),
        }

        await ctx.info(
            f"Optionen geladen: {len(top_themes)} Designs, {len(ordner)} Ordner."
        )
        return _success_response(optionen)

    except GammaAPIError as exc:
        logger.error("API-Fehler in praesentation_vorbereiten: %s", exc.message)
        return _error_response(exc.message)
    except Exception as exc:
        logger.error("Fehler in praesentation_vorbereiten: %s", exc)
        return _error_response(f"Unerwarteter Fehler: {exc}")


# ------------------------------------------------------------------ #
# Tool 2: designs_durchsuchen (ehemals gamma_themes_auflisten)
# ------------------------------------------------------------------ #

@mcp.tool(tags={"lookup"}, timeout=15.0)
async def designs_durchsuchen(
    ctx: Context,
    suchbegriff: str | None = None,
    limit: int = 10,
    after: str | None = None,
) -> str:
    """Durchsucht verfügbare Gamma-Designs (Themes) nach Name.

    praesentation_vorbereiten liefert bereits die 10 beliebtesten Designs.
    Nutze dieses Tool nur, wenn der Nutzer nach einem bestimmten Design
    sucht oder mehr Auswahl sehen möchte.

    Gibt nur id und name pro Design zurück. Verwende die id als
    theme_id in praesentation_erstellen.

    Args:
        suchbegriff: Optionaler Suchbegriff — filtert Designs nach Name
            (Groß-/Kleinschreibung egal). Ohne Suchbegriff werden die
            ersten Ergebnisse zurückgegeben.
        limit: Maximale Anzahl Ergebnisse (Standard: 10).
        after: Cursor für die nächste Seite (nextCursor aus vorherigem Aufruf).
    """
    try:
        client = _get_client(ctx)

        fetch_limit = 50 if suchbegriff else limit
        result = await client.list_themes(limit=fetch_limit, after=after)

        items = result.get("data", [])
        simplified = _simplify_items(items)

        if suchbegriff:
            needle = suchbegriff.lower()
            simplified = [
                t for t in simplified
                if needle in t.get("name", "").lower()
            ]

        truncated = simplified[:limit]

        response = {
            "designs": truncated,
            "anzahl": len(truncated),
            "hat_mehr": result.get("hasMore", False) or len(simplified) > limit,
        }
        if result.get("nextCursor"):
            response["naechster_cursor"] = result["nextCursor"]

        await ctx.info(f"{len(truncated)} Designs gefunden.")
        return _success_response(response)

    except GammaAPIError as exc:
        logger.error("API-Fehler in designs_durchsuchen: %s", exc.message)
        return _error_response(exc.message)
    except Exception as exc:
        logger.error("Fehler in designs_durchsuchen: %s", exc)
        return _error_response(f"Unerwarteter Fehler: {exc}")


# ------------------------------------------------------------------ #
# Tool 3: praesentation_erstellen (ehemals gamma_generieren)
# ------------------------------------------------------------------ #

@mcp.tool(tags={"core"}, timeout=360.0)
async def praesentation_erstellen(
    inhalt: str,
    ctx: Context,
    text_modus: str = "generate",
    format: str = "presentation",
    theme_id: str | None = None,
    anzahl_karten: int | None = None,
    karten_aufteilung: str = "auto",
    zusaetzliche_anweisungen: str | None = None,
    ordner_ids: list[str] | None = None,
    export_als: str | None = None,
    text_menge: str | None = None,
    text_ton: str | None = None,
    text_zielgruppe: str | None = None,
    text_sprache: str | None = None,
    bild_quelle: str | None = None,
    bild_modell: str | None = None,
    bild_stil: str | None = None,
    karten_dimensionen: str | None = None,
    workspace_zugriff: str | None = None,
    externer_zugriff: str | None = None,
    email_empfaenger: list[str] | None = None,
    email_zugriff: str | None = None,
) -> str:
    """Erstellt eine neue Gamma (Präsentation, Dokument, Social Post oder Webseite)
    und wartet automatisch auf Fertigstellung. Gibt die fertige URL direkt zurück.

    Rufe vorher praesentation_vorbereiten auf, um dem Nutzer Optionen anzubieten.

    Args:
        inhalt: Inhalt oder Prompt für die Gamma (1–400.000 Zeichen).
        text_modus: Umgang mit dem Inhalt — "generate" (KI schreibt neu),
            "condense" (kürzt zusammen) oder "preserve" (übernimmt wörtlich).
        format: Ausgabeformat — "presentation", "document", "social" oder
            "webpage". Standard: "presentation".
        theme_id: Optionale Design-ID (aus designs_durchsuchen oder
            praesentation_vorbereiten).
        anzahl_karten: Anzahl Karten/Folien (1–60 für Pro, 1–75 für Ultra).
        karten_aufteilung: Wie Inhalt auf Karten verteilt wird — "auto" oder
            "inputTextBreaks".
        zusaetzliche_anweisungen: Zusätzliche Anweisungen an die KI (max 500 Zeichen).
        ordner_ids: Liste von Ordner-IDs als Ziel (aus ordner_anzeigen).
        export_als: Sofort exportieren als "pdf" oder "pptx". Die Download-URLs
            sind temporär und verfallen nach kurzer Zeit.
        text_menge: Textmenge — "brief", "medium", "detailed" oder "extensive".
        text_ton: Tonalität, z.B. "professionell, inspirierend" (max 500 Zeichen).
        text_zielgruppe: Zielgruppe, z.B. "Geschäftsführer, Investoren" (max 500 Zeichen).
        text_sprache: Sprachcode, z.B. "de", "en", "fr".
        bild_quelle: Bildquelle — "aiGenerated", "unsplash", "webImages",
            "pictographic", "gifs", "placeholder" oder "noImages".
        bild_modell: KI-Bildmodell, z.B. "imagen-4-pro".
        bild_stil: Bildstil, z.B. "fotorealistisch" (max 500 Zeichen).
        karten_dimensionen: Kartengröße — "fluid", "16x9", "4x3", "pageless",
            "letter" oder "a4".
        workspace_zugriff: Workspace-Zugriff — "noAccess", "view", "comment",
            "edit" oder "fullAccess".
        externer_zugriff: Externer Zugriff — "noAccess", "view", "comment",
            "edit" oder "fullAccess".
        email_empfaenger: Liste von E-Mail-Adressen zum Teilen.
        email_zugriff: Zugriffslevel für E-Mail-Empfänger.
    """
    try:
        has_text_prefs = any([
            text_sprache, text_zielgruppe, text_ton,
            text_menge, zusaetzliche_anweisungen,
        ])
        if not has_text_prefs:
            (
                text_sprache, text_zielgruppe, text_ton,
                text_menge, zusaetzliche_anweisungen,
            ) = await _try_elicit_prefs(
                ctx, text_sprache, text_zielgruppe,
                text_ton, text_menge, zusaetzliche_anweisungen,
            )

        await ctx.info("Gamma wird generiert …")

        payload: dict = {
            "inputText": inhalt,
            "textMode": text_modus,
            "format": format,
            "cardSplit": karten_aufteilung,
        }

        if theme_id is not None:
            payload["themeId"] = theme_id
        if anzahl_karten is not None:
            payload["numCards"] = anzahl_karten
        if zusaetzliche_anweisungen is not None:
            payload["additionalInstructions"] = zusaetzliche_anweisungen
        if ordner_ids is not None:
            payload["folderIds"] = ordner_ids
        if export_als is not None:
            payload["exportAs"] = export_als

        text_options: dict = {}
        if text_menge is not None:
            text_options["amount"] = text_menge
        if text_ton is not None:
            text_options["tone"] = text_ton
        if text_zielgruppe is not None:
            text_options["audience"] = text_zielgruppe
        if text_sprache is not None:
            text_options["language"] = text_sprache
        if text_options:
            payload["textOptions"] = text_options

        image_options: dict = {}
        if bild_quelle is not None:
            image_options["source"] = bild_quelle
        if bild_modell is not None:
            image_options["model"] = bild_modell
        if bild_stil is not None:
            image_options["style"] = bild_stil
        if image_options:
            payload["imageOptions"] = image_options

        card_options: dict = {}
        if karten_dimensionen is not None:
            card_options["dimensions"] = karten_dimensionen
        if card_options:
            payload["cardOptions"] = card_options

        sharing_options: dict = {}
        if workspace_zugriff is not None:
            sharing_options["workspaceAccess"] = workspace_zugriff
        if externer_zugriff is not None:
            sharing_options["externalAccess"] = externer_zugriff
        if email_empfaenger is not None:
            email_opts: dict = {"recipients": email_empfaenger}
            if email_zugriff is not None:
                email_opts["access"] = email_zugriff
            sharing_options["emailOptions"] = email_opts
        if sharing_options:
            payload["sharingOptions"] = sharing_options

        client = _get_client(ctx)
        gen_result = await client.generate(payload)

        gen_id = gen_result.get("generationId")
        if not gen_id:
            logger.error("Keine generationId in API-Antwort: %s", gen_result)
            return _error_response("Keine generationId in der API-Antwort erhalten.")

        await ctx.info(f"Generation gestartet (ID: {gen_id}). Warte auf Fertigstellung …")

        result = await _poll_until_done(client, ctx, gen_id)

        final_status = result.get("status", "unknown")
        if final_status == "completed":
            url = result.get("gammaUrl", "")
            await ctx.info(f"Fertig! {url}")
        elif final_status == "failed":
            logger.error("Generation %s fehlgeschlagen: %s", gen_id, result)
            await ctx.warning("Gamma-Generierung fehlgeschlagen.")
        else:
            await ctx.info(f"Aktueller Status: {final_status}")

        return _success_response(result)

    except GammaAPIError as exc:
        logger.error("API-Fehler in praesentation_erstellen: %s", exc.message)
        return _error_response(exc.message)
    except Exception as exc:
        logger.error("Fehler in praesentation_erstellen: %s", exc)
        return _error_response(f"Unerwarteter Fehler: {exc}")


# ------------------------------------------------------------------ #
# Tool 4: praesentation_aus_vorlage (ehemals gamma_aus_vorlage_erstellen)
# ------------------------------------------------------------------ #

@mcp.tool(tags={"beta"}, timeout=360.0)
async def praesentation_aus_vorlage(
    gamma_id: str,
    prompt: str,
    ctx: Context,
    theme_id: str | None = None,
    ordner_ids: list[str] | None = None,
    export_als: str | None = None,
    bild_modell: str | None = None,
    bild_stil: str | None = None,
    workspace_zugriff: str | None = None,
    externer_zugriff: str | None = None,
    email_empfaenger: list[str] | None = None,
    email_zugriff: str | None = None,
) -> str:
    """Erstellt eine Gamma basierend auf einem bestehenden Template (Beta-API)
    und wartet automatisch auf Fertigstellung.

    Nutze dieses Tool, wenn der Nutzer eine existierende Gamma als Vorlage
    verwenden möchte. Die Gamma-ID findest du in der URL eines Gammas
    (Format: g_abcdef123456ghi).

    Args:
        gamma_id: ID des Template-Gammas (z.B. "g_abcdef123456ghi").
        prompt: Anweisungen und Inhalt für die neue Gamma.
        theme_id: Optionale Design-ID (aus designs_durchsuchen).
        ordner_ids: Ziel-Ordner-IDs.
        export_als: Sofort exportieren als "pdf" oder "pptx".
        bild_modell: KI-Bildmodell, z.B. "imagen-4-pro".
        bild_stil: Bildstil, z.B. "fotorealistisch".
        workspace_zugriff: Workspace-Zugriff — "noAccess", "view", "comment",
            "edit" oder "fullAccess".
        externer_zugriff: Externer Zugriff — "noAccess", "view", "comment",
            "edit" oder "fullAccess".
        email_empfaenger: Liste von E-Mail-Adressen zum Teilen.
        email_zugriff: Zugriffslevel für E-Mail-Empfänger.
    """
    try:
        await ctx.info("Gamma aus Vorlage wird generiert …")

        payload: dict = {
            "gammaId": gamma_id,
            "prompt": prompt,
        }

        if theme_id is not None:
            payload["themeId"] = theme_id
        if ordner_ids is not None:
            payload["folderIds"] = ordner_ids
        if export_als is not None:
            payload["exportAs"] = export_als

        image_options: dict = {}
        if bild_modell is not None:
            image_options["model"] = bild_modell
        if bild_stil is not None:
            image_options["style"] = bild_stil
        if image_options:
            payload["imageOptions"] = image_options

        sharing_options: dict = {}
        if workspace_zugriff is not None:
            sharing_options["workspaceAccess"] = workspace_zugriff
        if externer_zugriff is not None:
            sharing_options["externalAccess"] = externer_zugriff
        if email_empfaenger is not None:
            email_opts: dict = {"recipients": email_empfaenger}
            if email_zugriff is not None:
                email_opts["access"] = email_zugriff
            sharing_options["emailOptions"] = email_opts
        if sharing_options:
            payload["sharingOptions"] = sharing_options

        client = _get_client(ctx)
        gen_result = await client.generate_from_template(payload)

        gen_id = gen_result.get("generationId")
        if not gen_id:
            logger.error("Keine generationId in API-Antwort: %s", gen_result)
            return _error_response("Keine generationId in der API-Antwort erhalten.")

        await ctx.info(f"Generation aus Vorlage gestartet (ID: {gen_id}). Warte auf Fertigstellung …")

        result = await _poll_until_done(client, ctx, gen_id)

        final_status = result.get("status", "unknown")
        if final_status == "completed":
            url = result.get("gammaUrl", "")
            await ctx.info(f"Fertig! {url}")
        elif final_status == "failed":
            logger.error("Generation %s fehlgeschlagen: %s", gen_id, result)
            await ctx.warning("Gamma-Generierung fehlgeschlagen.")
        else:
            await ctx.info(f"Aktueller Status: {final_status}")

        return _success_response(result)

    except GammaAPIError as exc:
        logger.error("API-Fehler in praesentation_aus_vorlage: %s", exc.message)
        return _error_response(exc.message)
    except Exception as exc:
        logger.error("Fehler in praesentation_aus_vorlage: %s", exc)
        return _error_response(f"Unerwarteter Fehler: {exc}")


# ------------------------------------------------------------------ #
# Tool 5: ergebnis_abrufen (ehemals gamma_status_abrufen)
# ------------------------------------------------------------------ #

@mcp.tool(tags={"core"}, timeout=330.0)
async def ergebnis_abrufen(
    generation_id: str,
    ctx: Context,
    automatisch_warten: bool = True,
    max_wartezeit_sekunden: int = 300,
) -> str:
    """Prüft den Status einer Gamma-Generierung oder ruft das Ergebnis erneut ab.

    Normalerweise nicht nötig, da praesentation_erstellen und
    praesentation_aus_vorlage bereits automatisch auf Fertigstellung warten.
    Nutze dieses Tool nur, wenn ein Timeout aufgetreten ist oder du
    die URL/Downloads erneut abrufen möchtest.

    Args:
        generation_id: Die generationId aus einer vorherigen Generierung.
        automatisch_warten: Wenn True, wird automatisch gepollt bis
            completed/failed. Wenn False, wird nur der aktuelle Status
            einmal abgerufen.
        max_wartezeit_sekunden: Maximale Wartezeit in Sekunden (Standard: 300).
    """
    try:
        client = _get_client(ctx)

        if not automatisch_warten:
            result = await client.get_generation(generation_id)
            return _success_response(result)

        await ctx.info(f"Warte auf Fertigstellung (max. {max_wartezeit_sekunden}s) …")

        result = await _poll_until_done(client, ctx, generation_id, max_wartezeit_sekunden)

        final_status = result.get("status", "unknown")
        if final_status == "completed":
            url = result.get("gammaUrl", "")
            await ctx.info(f"Gamma fertig: {url}")
        elif final_status == "failed":
            logger.error("Generation %s fehlgeschlagen: %s", generation_id, result)
            await ctx.warning("Gamma-Generierung fehlgeschlagen.")
        else:
            await ctx.info(f"Aktueller Status: {final_status}")

        return _success_response(result)

    except GammaAPIError as exc:
        logger.error("API-Fehler in ergebnis_abrufen: %s", exc.message)
        return _error_response(exc.message)
    except Exception as exc:
        logger.error("Fehler in ergebnis_abrufen: %s", exc)
        return _error_response(f"Unerwarteter Fehler: {exc}")


# ------------------------------------------------------------------ #
# Tool 6: ordner_anzeigen (ehemals gamma_ordner_auflisten)
# ------------------------------------------------------------------ #

@mcp.tool(tags={"lookup"}, timeout=15.0)
async def ordner_anzeigen(
    ctx: Context,
    limit: int = 20,
    after: str | None = None,
) -> str:
    """Zeigt die Workspace-Ordner in Gamma an.

    praesentation_vorbereiten liefert bereits die Ordner mit.
    Nutze dieses Tool nur, wenn du mehr Ordner laden möchtest.

    Args:
        limit: Maximale Anzahl Ergebnisse (Standard: 20).
        after: Cursor für die nächste Seite (nextCursor aus vorherigem Aufruf).
    """
    try:
        client = _get_client(ctx)
        result = await client.list_folders(limit=limit, after=after)

        items = result.get("data", [])
        simplified = _simplify_items(items)

        response = {
            "ordner": simplified,
            "anzahl": len(simplified),
            "hat_mehr": result.get("hasMore", False),
        }
        if result.get("nextCursor"):
            response["naechster_cursor"] = result["nextCursor"]

        await ctx.info(f"{len(simplified)} Ordner geladen.")
        return _success_response(response)

    except GammaAPIError as exc:
        logger.error("API-Fehler in ordner_anzeigen: %s", exc.message)
        return _error_response(exc.message)
    except Exception as exc:
        logger.error("Fehler in ordner_anzeigen: %s", exc)
        return _error_response(f"Unerwarteter Fehler: {exc}")
