import json
import sys
from contextlib import asynccontextmanager

import httpx
from fastmcp import Context, FastMCP

from mcsci_hub import __version__
from mcsci_hub.cache import TTLCache
from mcsci_hub.clients.crossref import CrossRefClient
from mcsci_hub.clients.openalex import OpenAlexClient
from mcsci_hub.clients.scihub import SciHubClient
from mcsci_hub.clients.semantic_scholar import SemanticScholarClient
from mcsci_hub.config import settings
from mcsci_hub.tools import register_all
from mcsci_hub.tools.paper import aggregate_paper


@asynccontextmanager
async def lifespan(server: FastMCP):
    timeout = httpx.Timeout(settings.http_timeout, connect=10.0)

    async with (
        httpx.AsyncClient(timeout=timeout) as general_client,
        httpx.AsyncClient(timeout=timeout) as scihub_client,
    ):
        state = {
            "crossref": CrossRefClient(general_client, mailto=settings.crossref_mailto),
            "openalex": OpenAlexClient(general_client, mailto=settings.openalex_mailto),
            "s2": SemanticScholarClient(general_client, api_key=settings.s2_api_key),
            "scihub": SciHubClient(scihub_client, mirrors=settings.mirror_list),
            "cache": TTLCache(
                max_size=settings.cache_max_size,
                ttl=settings.cache_ttl,
            ),
        }
        yield state


mcp = FastMCP(
    "mcsci-hub",
    instructions=(
        "Academic paper search, metadata, PDFs, citations, and BibTeX. "
        "Aggregates CrossRef, OpenAlex, Semantic Scholar, and Sci-Hub. "
        "Pairs with mcp-pdf for full-text analysis of downloaded papers."
    ),
    lifespan=lifespan,
)

register_all(mcp)


# --- MCP Resources ---


@mcp.resource("scihub://mirrors")
async def mirrors_resource() -> str:
    """Configured Sci-Hub mirror domains used for PDF downloads.

    Returns a JSON list of mirror hostnames and the total count. Check this
    resource when fetch_pdf fails to understand which mirrors are configured.
    Mirrors are tried in order — the first reachable mirror wins.
    """
    mirrors = settings.mirror_list
    return json.dumps({"mirrors": mirrors, "count": len(mirrors)}, indent=2)


@mcp.resource("scihub://config")
async def config_resource() -> str:
    """Current server configuration and capabilities.

    Returns JSON with cache settings (max_size, TTL), HTTP timeout, PDF save
    directory, configured mirrors, API contact emails, and whether a Semantic
    Scholar API key is present (enables higher rate limits). Check this to
    understand where PDFs are saved and what data sources are fully enabled.
    """
    return json.dumps({
        "cache_max_size": settings.cache_max_size,
        "cache_ttl": settings.cache_ttl,
        "http_timeout": settings.http_timeout,
        "pdf_save_dir": settings.pdf_save_dir,
        "mirrors": settings.mirror_list,
        "crossref_mailto": settings.crossref_mailto,
        "openalex_mailto": settings.openalex_mailto,
        "has_s2_api_key": bool(settings.s2_api_key),
    }, indent=2)


@mcp.resource("doi://{doi_prefix}/{doi_suffix}")
async def paper_resource(doi_prefix: str, doi_suffix: str, ctx: Context) -> str:
    """Paper metadata by DOI, aggregated from CrossRef, OpenAlex, and Semantic Scholar.

    Read this resource to get full details for any paper — title, authors, year,
    journal, abstract, citation counts, fields of study, open access status, and
    Sci-Hub availability. Data is merged from multiple sources for completeness.

    URI format: doi://10.1038/nature12373 where the DOI prefix (10.1038) and
    suffix (nature12373) form the two path segments.

    After reading, use these tools for further actions:
    - fetch_pdf(doi) to download the full-text PDF
    - get_citations(doi) / get_references(doi) to explore the citation graph
    - get_bibtex(doi) for a LaTeX-ready citation entry
    """
    doi = f"{doi_prefix}/{doi_suffix}"
    state = ctx.request_context.lifespan_context

    cache: TTLCache = state["cache"]
    cached = cache.get(f"paper:{doi}")
    if cached:
        return cached

    paper = await aggregate_paper(
        doi,
        openalex=state["openalex"],
        s2=state["s2"],
        crossref=state["crossref"],
        scihub=state["scihub"],
    )

    if paper is None:
        return json.dumps({"error": f"Paper not found for DOI: {doi}"})

    result = json.dumps(paper.model_dump(), indent=2)
    cache.set(f"paper:{doi}", result)
    return result


# --- MCP Prompts ---


@mcp.prompt()
def literature_review(topic: str, depth: str = "standard") -> str:
    """Guided workflow for conducting a literature review on a topic.

    Composes search, metadata, citations, and optionally mcp-pdf for
    full-text analysis. Use depth='deep' for comprehensive analysis
    with PDF parsing.

    Args:
        topic: The research topic to review
        depth: 'standard' for metadata-based review, 'deep' for full-text analysis with mcp-pdf
    """
    steps = [
        f"## Literature Review: {topic}\n",
        "### Step 1: Search",
        f"Use `search_papers` with query: \"{topic}\"",
        "Review results and identify the most relevant papers"
        " (highest citation count, most recent).\n",
        "### Step 2: Key Papers",
        "For the top 3-5 papers, use `get_paper(doi)` to retrieve full metadata.",
        "Note the abstracts, citation counts, and fields of study.\n",
        "### Step 3: Citation Graph",
        "For the most-cited paper, use `get_citations(doi)` to find follow-up work.",
        "For the foundational paper, use `get_references(doi)` to trace intellectual lineage.\n",
        "### Step 4: Synthesis",
        "Organize findings by theme, methodology, or chronology.",
        "Identify gaps, controversies, and emerging directions.\n",
    ]

    if depth == "deep":
        steps.extend([
            "### Step 5: Full-Text Analysis (Deep Mode)",
            "For each key paper, use `fetch_pdf(doi)` to download the PDF.",
            "Then use **mcp-pdf** tools on the downloaded file:",
            "- `extract_text(file_path)` — get the full text for detailed reading",
            "- `extract_tables(file_path)` — extract data tables and results",
            "- `pdf_info(file_path)` — check page count and structure\n",
            "Analyze methodology sections, compare experimental designs,",
            "and extract quantitative findings for comparison.\n",
        ])

    steps.extend([
        "### Step 6: BibTeX",
        "Use `get_bibtex(doi)` for each cited paper to build a bibliography.",
    ])

    return "\n".join(steps)


@mcp.prompt()
def paper_deep_dive(doi: str) -> str:
    """Thorough analysis of a single paper using metadata + full-text PDF parsing.

    Combines mcsci-hub tools with mcp-pdf for comprehensive understanding.

    Args:
        doi: The DOI of the paper to analyze
    """
    return f"""## Deep Dive: {doi}

### Step 1: Metadata
Use `get_paper("{doi}")` to get full metadata — abstract, authors, citations, fields of study.

### Step 2: PDF
Use `fetch_pdf("{doi}")` to download the full-text PDF.

### Step 3: Full-Text Analysis (requires mcp-pdf)
Use mcp-pdf tools on the downloaded PDF:
- `extract_text(file_path)` — read the full paper
- `extract_tables(file_path)` — extract all data tables
- Focus on: methodology, key findings, limitations, future work

### Step 4: Context
Use `get_references("{doi}")` to understand what this paper builds on.
Use `get_citations("{doi}")` to see how it has been received and extended.

### Step 5: Summary
Synthesize: What problem does this solve? How? What are the key results?
What are the limitations? How has the field responded?
"""


@mcp.prompt()
def extract_findings(doi: str) -> str:
    """Focused extraction of key findings and data from a paper.

    Optimized for quickly pulling structured data, tables, and conclusions.

    Args:
        doi: The DOI of the paper to extract from
    """
    return f"""## Extract Findings: {doi}

### Step 1: Get the PDF
Use `fetch_pdf("{doi}")` to download the paper.

### Step 2: Extract Data (requires mcp-pdf)
Use mcp-pdf tools on the downloaded file:
- `extract_tables(file_path)` — get all tables with quantitative results
- `extract_text(file_path)` — get full text, focus on Results and Discussion sections

### Step 3: Structure
Organize extracted findings:
- **Main hypothesis/research question**
- **Key quantitative results** (with confidence intervals/p-values if reported)
- **Tables and figures** summarized
- **Authors' conclusions**
- **Stated limitations**
"""


def main():
    print(f"mcsci-hub v{__version__}", file=sys.stderr)
    mcp.run()
