import json

from fastmcp import Context, FastMCP

from mcsci_hub.clients.crossref import CrossRefClient
from mcsci_hub.clients.openalex import OpenAlexClient
from mcsci_hub.models import PaperResult


def register(mcp: FastMCP) -> None:
    @mcp.tool()
    async def search_papers(
        ctx: Context,
        query: str,
        max_results: int = 10,
        year_from: int | None = None,
        year_to: int | None = None,
        open_access_only: bool = False,
    ) -> str:
        """Search academic literature by title, keyword, topic, or natural language query.

        Returns a ranked list of papers with DOI, title, authors, year, journal,
        and citation count. Searches OpenAlex first (better semantic matching,
        includes abstracts and OA info), falls back to CrossRef.

        Use get_paper(doi) to get full details for any result.
        Use fetch_pdf(doi) to download the full-text PDF.

        Args:
            query: Search terms — titles, keywords, topics, or natural language
            max_results: Maximum number of results to return (1-50, default 10)
            year_from: Filter to papers published on or after this year
            year_to: Filter to papers published on or before this year
            open_access_only: If true, only return open access papers
        """
        max_results = max(1, min(max_results, 50))

        state = ctx.request_context.lifespan_context

        openalex: OpenAlexClient = state["openalex"]
        crossref: CrossRefClient = state["crossref"]

        await ctx.report_progress(progress=0, total=2)

        # Try OpenAlex first — better semantic search, has abstracts + OA info
        results: list[PaperResult] = []
        oa_items = await openalex.search(
            query, max_results=max_results, year_from=year_from,
            year_to=year_to, open_access_only=open_access_only,
        )
        await ctx.report_progress(progress=1, total=2)

        if oa_items:
            for item in oa_items:
                doi = OpenAlexClient.parse_doi(item)
                if not doi:
                    continue
                results.append(PaperResult(
                    doi=doi,
                    title=OpenAlexClient.parse_title(item),
                    authors=OpenAlexClient.parse_authors(item)[:5],
                    year=OpenAlexClient.parse_year(item),
                    journal=OpenAlexClient.parse_journal(item),
                    citation_count=item.get("cited_by_count"),
                    open_access=OpenAlexClient.is_oa(item),
                    source="openalex",
                ))

        # Fall back to CrossRef if OpenAlex returned nothing
        if not results:
            cr_items = await crossref.search(
                query, max_results=max_results, year_from=year_from, year_to=year_to,
            )
            for item in cr_items:
                doi = item.get("DOI", "")
                if not doi:
                    continue
                results.append(PaperResult(
                    doi=doi,
                    title=CrossRefClient.parse_title(item),
                    authors=CrossRefClient.parse_authors(item)[:5],
                    year=CrossRefClient.parse_year(item),
                    journal=CrossRefClient.parse_journal(item),
                    citation_count=item.get("is-referenced-by-count"),
                    open_access=False,
                    source="crossref",
                ))

        await ctx.report_progress(progress=2, total=2)

        if not results:
            return json.dumps({"results": [], "message": "No papers found for this query."})

        return json.dumps(
            {"results": [r.model_dump() for r in results], "count": len(results)},
            indent=2,
        )
