import json
from contextlib import asynccontextmanager
from unittest.mock import patch

import httpx
import respx
from fastmcp import Client, FastMCP

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.tools import register_all
from tests.conftest import SAMPLE_OPENALEX_WORK


def _make_server(pdf_dir: str) -> FastMCP:
    @asynccontextmanager
    async def test_lifespan(server: FastMCP):
        with patch("mcsci_hub.tools.pdf.settings") as mock_settings:
            mock_settings.pdf_save_dir = pdf_dir
            mock_settings.max_pdf_size = 100 * 1024 * 1024
            async with httpx.AsyncClient() as http_client:
                yield {
                    "crossref": CrossRefClient(http_client, mailto="test@test.com"),
                    "openalex": OpenAlexClient(http_client, mailto="test@test.com"),
                    "s2": SemanticScholarClient(http_client),
                    "scihub": SciHubClient(http_client, mirrors=["sci-hub.test"]),
                    "cache": TTLCache(max_size=100, ttl=60),
                }

    app = FastMCP("test-mcsci-hub", lifespan=test_lifespan)
    register_all(app)
    return app


@respx.mock
async def test_fetch_pdf_open_access(tmp_path):
    pdf_dir = str(tmp_path / "pdfs")

    respx.get("https://api.openalex.org/works/doi:10.1038/nature12373").mock(
        return_value=httpx.Response(200, json=SAMPLE_OPENALEX_WORK)
    )
    respx.get("https://arxiv.org/pdf/1706.03762").mock(
        return_value=httpx.Response(
            200,
            content=b"%PDF-1.4 fake content",
            headers={"content-type": "application/pdf"},
        )
    )

    async with Client(_make_server(pdf_dir)) as client:
        result = await client.call_tool("fetch_pdf", {"doi": "10.1038/nature12373"})
        data = json.loads(result.content[0].text)
        assert data["status"] == "success"
        assert data["source"] == "open_access"
        assert "mcp-pdf" in data["hint"]


@respx.mock
async def test_fetch_pdf_not_available(tmp_path):
    pdf_dir = str(tmp_path / "pdfs")

    respx.get("https://api.openalex.org/works/doi:10.0000/nope").mock(
        return_value=httpx.Response(404)
    )
    respx.get("https://sci-hub.test/10.0000/nope").mock(
        return_value=httpx.Response(200, text="<html><body>not found</body></html>")
    )

    async with Client(_make_server(pdf_dir)) as client:
        result = await client.call_tool("fetch_pdf", {"doi": "10.0000/nope"})
        data = json.loads(result.content[0].text)
        assert data["status"] == "error"
