"""Gen-Tools — Suche und Details via NCBI Gene Database und ClinVar."""

import httpx
from typing import Any


# NCBI E-utilities Basis-URL
NCBI_BASE = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
# Tool- und Email-Parameter für NCBI (höflicher Zugriff)
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)}


async def _ncbi_fetch_text(endpoint: str, params: dict) -> str:
    """NCBI E-utilities API Anfrage mit Text-Rückgabe (für Sequenzen)."""
    try:
        merged = {**NCBI_PARAMS, **params}
        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.text
    except Exception as e:
        return f"Fehler: {e}"


def register_gene_tools(mcp) -> None:
    """Registriert alle Gen- und Varianten-Tools."""

    @mcp.tool()
    async def search_genes(
        query: str,
        organism: str = "human",
        max_results: int = 10,
    ) -> dict:
        """Sucht nach Genen in der NCBI Gene Datenbank.

        Args:
            query: Suchbegriff (z.B. "BRCA1", "TP53", "insulin receptor")
            organism: Organismus (z.B. "human", "mouse", "rat"). Standard: human
            max_results: Maximale Ergebnisanzahl (1-20). Standard: 10
        """
        max_results = min(max(1, max_results), 20)
        # Suchanfrage mit Organismus-Filter
        search_term = f"{query}[Gene Name] AND {organism}[Organism]"

        # Schritt 1: IDs suchen
        search_data = await _ncbi_get(
            "esearch.fcgi",
            {"db": "gene", "term": search_term, "retmax": max_results},
        )
        if "error" in search_data:
            return {"fehler": search_data["error"]}

        ids = search_data.get("esearchresult", {}).get("idlist", [])
        if not ids:
            # Breiteren Fallback versuchen
            search_data = await _ncbi_get(
                "esearch.fcgi",
                {"db": "gene", "term": f"{query} AND {organism}[Organism]", "retmax": max_results},
            )
            ids = search_data.get("esearchresult", {}).get("idlist", [])

        if not ids:
            return {"fehler": f"Keine Gene für '{query}' in '{organism}' gefunden"}

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

        result_map = summary_data.get("result", {})
        gene_list = []
        for gene_id in ids:
            g = result_map.get(gene_id, {})
            if not g or gene_id == "uids":
                continue
            gene_list.append({
                "gen_id": gene_id,
                "name": g.get("name", ""),
                "vollstaendiger_name": g.get("description", ""),
                "organismus": g.get("organism", {}).get("scientificname", ""),
                "chromosom": g.get("chromosome", ""),
                "position": g.get("maplocation", ""),
                "zusammenfassung": (g.get("summary") or "")[:400],
                "ncbi_url": f"https://www.ncbi.nlm.nih.gov/gene/{gene_id}",
            })

        return {
            "suchbegriff": query,
            "organismus": organism,
            "gefunden": len(gene_list),
            "gene": gene_list,
        }

    @mcp.tool()
    async def get_gene_details(gene_id: str) -> dict:
        """Ruft detaillierte Informationen zu einem Gen via NCBI Gene ID ab.

        Args:
            gene_id: NCBI Gene ID (z.B. "672" für BRCA1, "7157" für TP53)
        """
        data = await _ncbi_get("esummary.fcgi", {"db": "gene", "id": gene_id})
        if "error" in data:
            return {"fehler": data["error"]}

        result = data.get("result", {}).get(gene_id, {})
        if not result:
            return {"fehler": f"Gen mit ID '{gene_id}' nicht gefunden"}

        # Verwandte Gene (Gene Neighbors) aus LocationHistList
        orthologe = []
        for orth in result.get("orthologs", [])[:5]:
            orthologe.append({
                "gen_id": orth.get("geneids", [""])[0],
                "organismus": orth.get("scientificname", ""),
            })

        return {
            "gen_id": gene_id,
            "symbol": result.get("name", ""),
            "vollstaendiger_name": result.get("description", ""),
            "organismus": result.get("organism", {}).get("scientificname", ""),
            "chromosom": result.get("chromosome", ""),
            "position": result.get("maplocation", ""),
            "typ": result.get("genetype", ""),
            "zusammenfassung": result.get("summary", ""),
            "aliases": result.get("otheraliases", ""),
            "andere_bezeichnungen": result.get("otherdesignations", ""),
            "ncbi_url": f"https://www.ncbi.nlm.nih.gov/gene/{gene_id}",
        }

    @mcp.tool()
    async def search_clinvar_variants(
        gene_symbol: str,
        significance: str = "pathogenic",
        max_results: int = 10,
    ) -> dict:
        """Sucht klinische Varianten (Mutationen) für ein Gen via NCBI ClinVar.

        Args:
            gene_symbol: Gen-Symbol (z.B. "BRCA1", "TP53", "CFTR")
            significance: Klinische Signifikanz — "pathogenic", "benign", "uncertain" oder "all". Standard: pathogenic
            max_results: Maximale Ergebnisanzahl (1-20). Standard: 10
        """
        max_results = min(max(1, max_results), 20)

        # Suchanfrage aufbauen
        if significance == "all":
            term = f"{gene_symbol}[gene]"
        else:
            sig_map = {
                "pathogenic": "Pathogenic[ClinSig]",
                "benign": "Benign[ClinSig]",
                "uncertain": "Uncertain significance[ClinSig]",
            }
            sig_filter = sig_map.get(significance, "Pathogenic[ClinSig]")
            term = f"{gene_symbol}[gene] AND {sig_filter}"

        # IDs suchen
        search_data = await _ncbi_get(
            "esearch.fcgi",
            {"db": "clinvar", "term": term, "retmax": max_results},
        )
        if "error" in search_data:
            return {"fehler": search_data["error"]}

        ids = search_data.get("esearchresult", {}).get("idlist", [])
        if not ids:
            return {
                "fehler": f"Keine Varianten für '{gene_symbol}' mit Signifikanz '{significance}' gefunden",
                "tipp": "Versuche significance='all' für alle Varianten",
            }

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

        result_map = summary_data.get("result", {})
        varianten = []
        for var_id in ids:
            v = result_map.get(var_id, {})
            if not v or var_id == "uids":
                continue

            # Signifikanz aus clinical_significance
            clin_sig = v.get("clinical_significance", {})
            sig_label = clin_sig.get("description", "")

            varianten.append({
                "varianten_id": var_id,
                "name": v.get("title", ""),
                "gen": gene_symbol,
                "typ": v.get("obj_type", ""),
                "klinische_signifikanz": sig_label,
                "review_status": clin_sig.get("review_status", ""),
                "bedingung": v.get("trait_set", [{}])[0].get("trait_name", "") if v.get("trait_set") else "",
                "letzte_bewertung": clin_sig.get("last_evaluated", ""),
                "clinvar_url": f"https://www.ncbi.nlm.nih.gov/clinvar/variation/{var_id}/",
            })

        return {
            "gen": gene_symbol,
            "signifikanz_filter": significance,
            "gefunden": len(varianten),
            "varianten": varianten,
        }

    @mcp.tool()
    async def get_taxonomy(
        taxon: str,
    ) -> dict:
        """Ruft taxonomische Klassifikation einer Spezies via NCBI Taxonomy ab.

        Args:
            taxon: Artname oder Taxonomie-ID (z.B. "Homo sapiens", "9606", "Mus musculus")
        """
        # Prüfen ob ID oder Name
        if taxon.isdigit():
            tax_id = taxon
        else:
            # Name zu ID auflösen
            search_data = await _ncbi_get(
                "esearch.fcgi",
                {"db": "taxonomy", "term": taxon},
            )
            if "error" in search_data:
                return {"fehler": search_data["error"]}
            ids = search_data.get("esearchresult", {}).get("idlist", [])
            if not ids:
                return {"fehler": f"Taxonomie für '{taxon}' nicht gefunden"}
            tax_id = ids[0]

        # Details abrufen
        summary_data = await _ncbi_get(
            "esummary.fcgi",
            {"db": "taxonomy", "id": tax_id},
        )
        if "error" in summary_data:
            return {"fehler": summary_data["error"]}

        t = summary_data.get("result", {}).get(tax_id, {})
        if not t:
            return {"fehler": f"Taxonomie-ID '{tax_id}' nicht gefunden"}

        return {
            "taxonomie_id": tax_id,
            "wissenschaftlicher_name": t.get("scientificname", ""),
            "gemeiner_name": t.get("commonname", ""),
            "rang": t.get("rank", ""),
            "abteilung": t.get("division", ""),
            "genetischer_code": t.get("gencodeId", ""),
            "mitochondrialer_code": t.get("mgcodeId", ""),
            "ncbi_url": f"https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id={tax_id}",
        }
