import json

from fastmcp import Context, FastMCP

from mcsci_hub.clients.semantic_scholar import SemanticScholarClient
from mcsci_hub.models import CitationEntry


def _parse_s2_citation(entry: dict, direction: str) -> CitationEntry | None:
    """Parse a Semantic Scholar citation/reference entry."""
    paper = entry.get("citingPaper" if direction == "citations" else "citedPaper", {})
    if not paper:
        return None
    doi = SemanticScholarClient.parse_doi(paper)
    title = paper.get("title", "")
    if not title:
        return None
    return CitationEntry(
        doi=doi,
        title=title,
        authors=SemanticScholarClient.parse_authors(paper)[:5],
        year=paper.get("year"),
        journal=paper.get("venue", ""),
        citation_count=paper.get("citationCount"),
        is_influential=entry.get("isInfluential"),
    )


def register(mcp: FastMCP) -> None:
    @mcp.tool()
    async def get_citations(ctx: Context, doi: str, max_results: int = 20) -> str:
        """Get forward citations — papers that cite the given paper.

        Useful for finding follow-up work, replications, and how an idea
        has been extended. Results include an 'is_influential' flag from
        Semantic Scholar indicating whether the citation was meaningful
        (not just a passing mention).

        Use get_paper(doi) on any result to get full details.

        Args:
            doi: The DOI of the paper whose citations to retrieve
            max_results: Maximum number of citations to return (1-100, default 20)
        """
        max_results = max(1, min(max_results, 100))

        state = ctx.request_context.lifespan_context
        s2: SemanticScholarClient = state["s2"]

        raw = await s2.get_citations(doi, max_results=max_results)
        entries = []
        for item in raw:
            entry = _parse_s2_citation(item, "citations")
            if entry:
                entries.append(entry)

        if not entries:
            return json.dumps({
                "doi": doi,
                "citations": [],
                "message": "No citations found. The paper may be very recent or not indexed.",
            })

        return json.dumps({
            "doi": doi,
            "citations": [e.model_dump() for e in entries],
            "count": len(entries),
        }, indent=2)

    @mcp.tool()
    async def get_references(ctx: Context, doi: str, max_results: int = 20) -> str:
        """Get backward references — papers cited by the given paper.

        Useful for understanding what prior work a paper builds on,
        finding foundational papers, and tracing intellectual lineage.

        Use get_paper(doi) on any result to get full details.

        Args:
            doi: The DOI of the paper whose references to retrieve
            max_results: Maximum number of references to return (1-100, default 20)
        """
        max_results = max(1, min(max_results, 100))

        state = ctx.request_context.lifespan_context
        s2: SemanticScholarClient = state["s2"]

        raw = await s2.get_references(doi, max_results=max_results)
        entries = []
        for item in raw:
            entry = _parse_s2_citation(item, "references")
            if entry:
                entries.append(entry)

        if not entries:
            return json.dumps({
                "doi": doi,
                "references": [],
                "message": "No references found.",
            })

        return json.dumps({
            "doi": doi,
            "references": [e.model_dump() for e in entries],
            "count": len(entries),
        }, indent=2)
