"""Literatur-Tools — Wissenschaftliche Artikel via PubMed (NCBI E-utilities)."""

import httpx
from typing import Any


# NCBI E-utilities Basis-URL
NCBI_BASE = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
NCBI_PARAMS = {"tool": "bioinformatics-mcp-server", "email": "agent@aiagentkarl.com"}


async def _ncbi_get(endpoint: str, params: dict) -> dict[str, Any]:
    """NCBI E-utilities API Anfrage."""
    try:
        merged = {**NCBI_PARAMS, **params, "retmode": "json"}
        async with httpx.AsyncClient(timeout=20.0) as client:
            response = await client.get(f"{NCBI_BASE}/{endpoint}", params=merged)
            response.raise_for_status()
            return response.json()
    except Exception as e:
        return {"error": str(e)}


def register_literature_tools(mcp) -> None:
    """Registriert alle Literatur-Tools."""

    @mcp.tool()
    async def search_pubmed(
        query: str,
        max_results: int = 10,
        sort: str = "relevance",
        years_back: int = 0,
    ) -> dict:
        """Sucht wissenschaftliche Artikel in PubMed.

        Args:
            query: Suchbegriff (z.B. "BRCA1 breast cancer therapy", "CRISPR gene editing")
            max_results: Maximale Ergebnisanzahl (1-20). Standard: 10
            sort: Sortierung — "relevance" oder "date". Standard: relevance
            years_back: Nur Artikel der letzten N Jahre (0 = alle). Standard: 0
        """
        max_results = min(max(1, max_results), 20)

        # Datumsfilter
        search_term = query
        if years_back > 0:
            from datetime import datetime
            aktuelles_jahr = datetime.now().year
            von_jahr = aktuelles_jahr - years_back
            search_term = f"{query} AND ({von_jahr}:{aktuelles_jahr}[pdat])"

        sort_param = "relevance" if sort == "relevance" else "pub+date"

        # Schritt 1: Artikel-IDs suchen
        search_data = await _ncbi_get(
            "esearch.fcgi",
            {
                "db": "pubmed",
                "term": search_term,
                "retmax": max_results,
                "sort": sort_param,
                "usehistory": "y",
            },
        )
        if "error" in search_data:
            return {"fehler": search_data["error"]}

        ids = search_data.get("esearchresult", {}).get("idlist", [])
        count = search_data.get("esearchresult", {}).get("count", "0")

        if not ids:
            return {
                "fehler": f"Keine Artikel für '{query}' in PubMed gefunden",
                "tipp": "Versuche weniger spezifische Suchbegriffe oder Englisch",
            }

        # Schritt 2: Zusammenfassungen abrufen
        summary_data = await _ncbi_get(
            "esummary.fcgi",
            {"db": "pubmed", "id": ",".join(ids)},
        )
        if "error" in summary_data:
            return {"fehler": summary_data["error"]}

        result_map = summary_data.get("result", {})
        artikel = []

        for pmid in ids:
            a = result_map.get(pmid, {})
            if not a or pmid == "uids":
                continue

            # Autoren extrahieren
            autoren = []
            for author in a.get("authors", [])[:5]:
                autoren.append(author.get("name", ""))

            artikel.append({
                "pmid": pmid,
                "titel": a.get("title", ""),
                "autoren": autoren,
                "journal": a.get("fulljournalname", a.get("source", "")),
                "erscheinungsjahr": a.get("pubdate", "")[:4],
                "doi": a.get("elocationid", ""),
                "abstract_url": f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/",
            })

        return {
            "suchbegriff": query,
            "gesamt_treffer": int(count),
            "angezeigt": len(artikel),
            "sortierung": sort,
            "artikel": artikel,
        }

    @mcp.tool()
    async def get_pubmed_abstract(pmid: str) -> dict:
        """Ruft den vollständigen Abstract eines PubMed-Artikels ab.

        Args:
            pmid: PubMed ID (z.B. "33461210", "36257307")
        """
        # Abstract via efetch abrufen
        try:
            merged = {**NCBI_PARAMS, "db": "pubmed", "id": pmid, "rettype": "abstract", "retmode": "text"}
            async with httpx.AsyncClient(timeout=20.0) as client:
                response = await client.get(f"{NCBI_BASE}/efetch.fcgi", params=merged)
                response.raise_for_status()
                abstract_text = response.text
        except Exception as e:
            return {"fehler": str(e)}

        # Zusätzliche Metadaten
        summary_data = await _ncbi_get("esummary.fcgi", {"db": "pubmed", "id": pmid})
        meta = summary_data.get("result", {}).get(pmid, {})

        autoren = []
        for author in meta.get("authors", []):
            autoren.append(author.get("name", ""))

        return {
            "pmid": pmid,
            "titel": meta.get("title", ""),
            "autoren": autoren,
            "journal": meta.get("fulljournalname", meta.get("source", "")),
            "erscheinungsdatum": meta.get("pubdate", ""),
            "doi": meta.get("elocationid", ""),
            "abstract": abstract_text.strip(),
            "pubmed_url": f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/",
        }

    @mcp.tool()
    async def get_pubmed_citations(
        pmid: str,
        max_results: int = 10,
    ) -> dict:
        """Ruft Artikel ab, die einen bestimmten PubMed-Artikel zitieren.

        Args:
            pmid: PubMed ID des zitierten Artikels (z.B. "33461210")
            max_results: Maximale Ergebnisanzahl (1-20). Standard: 10
        """
        max_results = min(max(1, max_results), 20)

        # elink: Artikel finden die diesen zitieren
        try:
            params = {
                **NCBI_PARAMS,
                "dbfrom": "pubmed",
                "db": "pubmed",
                "id": pmid,
                "linkname": "pubmed_pubmed_citedin",
                "retmode": "json",
            }
            async with httpx.AsyncClient(timeout=20.0) as client:
                response = await client.get(f"{NCBI_BASE}/elink.fcgi", params=params)
                response.raise_for_status()
                link_data = response.json()
        except Exception as e:
            return {"fehler": str(e)}

        # IDs aus linksets extrahieren
        citing_ids = []
        for linkset in link_data.get("linksets", []):
            for lsd in linkset.get("linksetdbs", []):
                if lsd.get("linkname") == "pubmed_pubmed_citedin":
                    citing_ids = lsd.get("links", [])[:max_results]
                    break

        if not citing_ids:
            return {
                "pmid": pmid,
                "zitiert_von": 0,
                "artikel": [],
                "hinweis": "Keine Zitationen in PubMed gefunden (nur indexierte Artikel werden erfasst)",
            }

        # Zusammenfassungen der zitierenden Artikel
        id_str = ",".join(str(i) for i in citing_ids)
        summary_data = await _ncbi_get("esummary.fcgi", {"db": "pubmed", "id": id_str})
        result_map = summary_data.get("result", {})

        artikel = []
        for cid in citing_ids:
            cid_str = str(cid)
            a = result_map.get(cid_str, {})
            if not a or cid_str == "uids":
                continue
            autoren = [auth.get("name", "") for auth in a.get("authors", [])[:3]]
            artikel.append({
                "pmid": cid_str,
                "titel": a.get("title", ""),
                "autoren": autoren,
                "journal": a.get("fulljournalname", a.get("source", "")),
                "erscheinungsjahr": a.get("pubdate", "")[:4],
                "url": f"https://pubmed.ncbi.nlm.nih.gov/{cid_str}/",
            })

        return {
            "original_pmid": pmid,
            "gefundene_zitationen": len(artikel),
            "artikel": artikel,
        }
