"""Main MCP server for PapersWithCode"""
import io
from urllib.parse import urlencode
import httpx
import requests
from mcp.server.fastmcp import FastMCP
from PyPDF2 import PdfReader

mcp = FastMCP("Papers With Code MCP Interface")
BASE_URL = "https://paperswithcode.com/api/v1"

def encode_non_null_params(params: dict) -> str:
    """Encode non-null URL parameters for the API"""
    if params:
        updated_params = {k: v for k, v in params.items() if v is not None}
        return urlencode(updated_params)
    return ""

async def get_all_results(url: str) -> list:
    """Helper function to fetch all paginated results from a PapersWithCode endpoint"""
    all_results = []
    while url:
        async with httpx.AsyncClient() as client:
            response = await client.get(url)
            data = response.json()
            all_results.extend(data.get("results", []))
            url = data.get("next")
    return all_results

@mcp.tool()
async def search_research_areas(name: str) -> dict:
    """Search for research areas that exist in PapersWithCode"""
    params = {"name": name}
    url = f"{BASE_URL}/areas/?{encode_non_null_params(params)}"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def get_research_area(area_id: str) -> dict:
    """Get a research area by ID in PapersWithCode"""
    url = f"{BASE_URL}/areas/{area_id}/"
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.json()

@mcp.tool()
async def list_research_area_tasks(area_id: str) -> dict:
    """List the tasks for a given research area ID in PapersWithCode"""
    params = {"area": area_id}
    url = f"{BASE_URL}/tasks/?{encode_non_null_params(params)}"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def search_authors(full_name: str) -> dict:
    """Search for authors by name in PapersWithCode"""
    params = {"full_name": full_name}
    url = f"{BASE_URL}/authors/?{encode_non_null_params(params)}"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def get_paper_author(author_id: str) -> dict:
    """Get a paper author by ID in PapersWithCode"""
    url = f"{BASE_URL}/authors/{author_id}/"
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.json()

@mcp.tool()
async def list_papers_by_author_id(author_id: str) -> dict:
    """List the papers for a given author ID in PapersWithCode"""
    url = f"{BASE_URL}/authors/{author_id}/papers/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def list_papers_by_author_name(author_name: str) -> dict:
    """List the papers written by a given author name in PapersWithCode"""
    authors_response = await search_authors(author_name)
    if not authors_response.get("results"):
        return {"results": []}
    author_id = authors_response["results"][0]["id"]
    return await list_papers_by_author_id(author_id)

@mcp.tool()
async def list_conferences(conference_name: str | None = None) -> dict:
    """List the conferences in PapersWithCode"""
    params = {"name": conference_name}
    url = f"{BASE_URL}/conferences/?{encode_non_null_params(params)}"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def get_conference(conference_id: str) -> dict:
    """Get a conference by ID in PapersWithCode"""
    url = f"{BASE_URL}/conferences/{conference_id}/"
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.json()

@mcp.tool()
async def list_conference_proceedings(conference_id: str) -> dict:
    """List the proceedings for a given conference ID in PapersWithCode"""
    url = f"{BASE_URL}/conferences/{conference_id}/proceedings/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def get_conference_proceeding(conference_id: str, proceeding_id: str) -> dict:
    """Get a proceeding by ID in PapersWithCode"""
    url = f"{BASE_URL}/conferences/{conference_id}/proceedings/{proceeding_id}/"
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.json()

@mcp.tool()
async def list_conference_papers(conference_id: str, proceeding_id: str) -> dict:
    """List the papers for a given conference ID and proceeding ID in PapersWithCode"""
    url = f"{BASE_URL}/conferences/{conference_id}/proceedings/{proceeding_id}/papers/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def search_papers(abstract: str | None = None, title: str | None = None, arxiv_id: str | None = None) -> dict:
    """Search for a paper in PapersWithCode"""
    params = {"abstract": abstract, "title": title, "arxiv_id": arxiv_id}
    url = f"{BASE_URL}/papers/?{encode_non_null_params(params)}"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def get_paper(paper_id: str) -> dict:
    """Get a paper by ID in PapersWithCode"""
    url = f"{BASE_URL}/papers/{paper_id}/"
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.json()

@mcp.tool()
async def list_paper_repositories(paper_id: str) -> dict:
    """List the repositories for a given paper ID in PapersWithCode"""
    url = f"{BASE_URL}/papers/{paper_id}/repositories/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def list_paper_datasets(paper_id: str) -> dict:
    """List the datasets for a given paper ID in PapersWithCode"""
    url = f"{BASE_URL}/papers/{paper_id}/datasets/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def list_paper_methods(paper_id: str) -> dict:
    """List the methods for a given paper ID in PapersWithCode"""
    url = f"{BASE_URL}/papers/{paper_id}/methods/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def list_paper_results(paper_id: str) -> dict:
    """List the results for a given paper ID in PapersWithCode"""
    url = f"{BASE_URL}/papers/{paper_id}/results/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def list_paper_tasks(paper_id: str) -> dict:
    """List the tasks for a given paper ID in PapersWithCode"""
    url = f"{BASE_URL}/papers/{paper_id}/tasks/"
    results = await get_all_results(url)
    return {"results": results}

@mcp.tool()
async def read_paper_from_url(paper_url: str) -> dict:
    """Explain a paper by URL in PapersWithCode"""
    try:
        response = requests.get(paper_url)
        if response.headers.get('content-type') == 'application/pdf':
            pdf_content = io.BytesIO(response.content)
            reader = PdfReader(pdf_content)
            text = ""
            for page in reader.pages:
                text += page.extract_text()
            return {"text": text, "type": "pdf"}
        else:
            return {"text": response.text, "type": "html"}
    except Exception as e:
        return {"error": str(e), "type": "error"}

def main():
    """Main entry point for the MCP server."""
    mcp.run()

if __name__ == "__main__":
    main()
