"""MCP server for paper2md (optional dependency)."""
from __future__ import annotations

import asyncio
from pathlib import Path

try:
    from fastmcp import FastMCP, Context
except ImportError:
    FastMCP = None
    Context = None


def create_server():
    if FastMCP is None:
        raise ImportError("fastmcp is required: pip install paper2md[mcp]")

    mcp = FastMCP("paper2md")

    def _make_progress_callback(ctx, loop):
        """Create a progress callback that logs via MCP context.

        The callback will be invoked from a thread executor, so it schedules
        the async ctx.info() call back onto the event loop using
        asyncio.run_coroutine_threadsafe.
        """
        if ctx is None:
            return None
        def progress_cb(stage: str, current: int, total: int):
            try:
                asyncio.run_coroutine_threadsafe(
                    ctx.info(f"{stage}: {current}/{total}"),
                    loop,
                )
            except Exception:
                pass
        return progress_cb

    @mcp.tool()
    async def convert_pdf(path: str, ctx: Context = None) -> str:
        """Convert a research paper PDF to Markdown.

        Returns the full Markdown text of the converted paper.
        """
        from .core import convert
        from .types import PipelineConfig

        pdf_path = Path(path)
        if not pdf_path.is_file():
            return f"Error: file not found: {path}"
        try:
            loop = asyncio.get_running_loop()
            config = PipelineConfig(progress_callback=_make_progress_callback(ctx, loop))
            result = await loop.run_in_executor(None, convert, pdf_path, config)
            return result.markdown
        except Exception as e:
            return f"Error converting PDF: {e}"

    @mcp.tool()
    async def convert_pdf_structured(
        path: str,
        include_markdown: bool = True,
        include_references: bool = False,
        max_references: int = 20,
        ctx: Context = None,
    ) -> dict:
        """Convert a PDF and return structured result with metadata.

        Args:
            path: Path to the PDF file.
            include_markdown: Whether to include the full markdown text (default True).
                Set to False to get only metadata, which is much smaller.
            include_references: Whether to include reference details (default False).
            max_references: Maximum number of references to include (default 20).
        """
        from .core import convert
        from .types import PipelineConfig

        pdf_path = Path(path)
        if not pdf_path.is_file():
            return {"error": f"File not found: {path}"}
        try:
            loop = asyncio.get_running_loop()
            config = PipelineConfig(progress_callback=_make_progress_callback(ctx, loop))
            result = await loop.run_in_executor(None, convert, pdf_path, config)
        except Exception as e:
            return {"error": f"Conversion failed: {e}"}

        response: dict = {
            "title": result.title,
            "authors": result.authors,
            "abstract": result.abstract,
            "page_count": result.page_count,
            "figure_count": len(result.figures),
            "table_count": len(result.tables),
            "reference_count": len(result.references),
            "figure_captions": [
                f.caption for f in result.figures if f.caption
            ],
            "warnings": result.warnings,
        }

        if include_markdown:
            response["markdown"] = result.markdown

        if include_references:
            max_references = max(0, max_references)
            refs = result.references[:max_references]
            response["references"] = [
                {
                    "raw": r.raw,
                    "authors": r.authors,
                    "year": r.year,
                    "title": r.title,
                }
                for r in refs
            ]

        return response

    @mcp.tool()
    async def extract_metadata(path: str, include_references: bool = True, max_references: int = 50, ctx: Context = None) -> dict:
        """Extract structured metadata from a paper WITHOUT the full markdown.

        Returns title, authors, abstract, section headings (table of contents),
        and optionally the reference list. This is the most efficient tool for
        understanding a paper's structure without transferring the full text.

        Args:
            path: Path to the PDF file.
            include_references: Whether to include parsed references (default True).
            max_references: Maximum number of references to return (default 50).
        """
        from .core import convert
        from .types import BlockType, PipelineConfig

        pdf_path = Path(path)
        if not pdf_path.is_file():
            return {"error": f"File not found: {path}"}
        try:
            loop = asyncio.get_running_loop()
            config = PipelineConfig(progress_callback=_make_progress_callback(ctx, loop))
            result = await loop.run_in_executor(None, convert, pdf_path, config)
        except Exception as e:
            return {"error": f"Conversion failed: {e}"}

        # Extract section headings from the markdown for a table of contents
        sections: list[dict] = []
        for line in result.markdown.split("\n"):
            stripped = line.strip()
            if stripped.startswith("## "):
                sections.append({"level": 2, "title": stripped[3:].strip()})
            elif stripped.startswith("### "):
                sections.append({"level": 3, "title": stripped[4:].strip()})
            elif stripped.startswith("#### "):
                sections.append({"level": 4, "title": stripped[5:].strip()})

        response: dict = {
            "title": result.title,
            "authors": result.authors,
            "abstract": result.abstract,
            "page_count": result.page_count,
            "sections": sections,
            "figure_count": len(result.figures),
            "figure_captions": [
                f.caption for f in result.figures if f.caption
            ],
            "table_count": len(result.tables),
            "table_captions": [
                t.caption for t in result.tables if t.caption
            ],
            "reference_count": len(result.references),
            "warnings": result.warnings,
        }

        if include_references:
            max_references = max(0, max_references)
            refs = result.references[:max_references]
            response["references"] = [
                {
                    "raw": r.raw,
                    "authors": r.authors,
                    "year": r.year,
                    "title": r.title,
                }
                for r in refs
            ]

        return response

    return mcp


if __name__ == "__main__":
    server = create_server()
    server.run()
