from __future__ import annotations

from typing import Annotated

from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent, ToolAnnotations
from pydantic import Field

from .. import routes
from ..client import ApiClient
from ..decorators import handle_api_errors
from .constants import FEEDBACK_INSTRUCTION, SOURCE, STATUS_READY


def register(
    mcp: FastMCP,
    client: ApiClient,
    *,
    source_type: str,
    read_hint: ToolAnnotations,
    write_hint: ToolAnnotations,
    track,
) -> None:
    @mcp.tool(annotations=read_hint.model_copy(update={"title": "Get Topic Knowledge"}))
    @handle_api_errors
    @track
    async def get_topic_knowledge(
        topic_id: Annotated[str, Field(description="The topic ID (from list_topics).")],
        cursor: Annotated[
            str | None, Field(description="Pagination cursor from a previous call (omit for first page).", default=None)
        ] = None,
        client_hint: Annotated[
            str | None,
            Field(
                description="Client type hint to optimize response size. Values: claude-code, claude, chatgpt, gemini.",
                default=None,
            ),
        ] = None,
        session_token: Annotated[
            str | None,
            Field(
                description="Session token from a previous call. Pass it back to continue the session.", default=None
            ),
        ] = None,
    ) -> list[TextContent]:
        """Get saved knowledge for a specific topic.
        Call list_topics first to get the topic ID.

        Small pieces of knowledge include full content inline. Larger pieces of knowledge
        and file uploads include a summary and a file URL — download the file
        to access the full content.

        If has_more is true, call again with the returned cursor to get the next page.
        """
        params: dict = {"source": SOURCE, "status": STATUS_READY}
        if client_hint:
            params["client_hint"] = client_hint
        if session_token:
            params["session_token"] = session_token
        if cursor:
            params["cursor"] = cursor
        data = await client.get(routes.topic_knowledge(topic_id), params)

        items = data.get("items", [])
        if not items:
            return [TextContent(type="text", text="No knowledge saved for this topic.")]

        blocks = []
        file_items = [item for item in items if item.get("file_url")]
        has_pdfs = any(item.get("mime_type") == "application/pdf" for item in file_items)

        if file_items and not cursor:
            instructions = (
                "INSTRUCTIONS: You MUST download file URLs below to reference their content in the conversation. "
                "Use a tool like curl to download files, as web fetch tools may not accept MCP-provided URLs. "
                "If a download fails or isn't appearing in the UI, the user may need to allow the file hosting domain."
            )
            if has_pdfs:
                instructions += (
                    " For PDFs, render pages as images and parse visually to get the most context"
                    " — text extraction alone may miss important details."
                )
            blocks.append(TextContent(type="text", text=instructions))

        for item in items:
            parts = [f"### {item['title']} (id: {item['id']})"]
            if item.get("file_url"):
                if item.get("summary"):
                    parts.append(item["summary"])
                parts.append(f"File: {item['file_url']}")
            elif item.get("content"):
                parts.append(item["content"])
            if item.get("url"):
                parts.append(f"(source: {item['url']})")
            blocks.append(TextContent(type="text", text="\n".join(parts)))

        if data.get("has_more"):
            shown = data.get("shown", len(items))
            total = data.get("total")
            progress = f"Shown {shown} of {total}. " if total else ""
            blocks.append(
                TextContent(
                    type="text",
                    text=f'{progress}Call again with cursor="{data["next_cursor"]}" to get the next page.',
                )
            )

        session_token = data.get("session_token")
        if session_token:
            blocks.append(TextContent(type="text", text=f"Session: {session_token}"))

        if data.get("feedback_enabled"):
            blocks.append(TextContent(type="text", text=FEEDBACK_INSTRUCTION))

        return blocks

    @mcp.tool(annotations=write_hint.model_copy(update={"title": "Add Knowledge"}))
    @handle_api_errors
    @track
    async def add_knowledge(
        topic_id: Annotated[
            str, Field(description="The topic ID to add knowledge to (from list_topics or create_topic).")
        ],
        title: Annotated[str, Field(description="A one-liner describing this piece of knowledge.")],
        content: Annotated[str, Field(description="The knowledge content to save.")],
    ) -> list[TextContent]:
        """Save text knowledge to a topic. This is for text content only, not file uploads.

        Only call this when the user explicitly asks to save knowledge.
        Always confirm the topic (name and ID), title, and content with the user before calling this tool.
        Call list_topics or create_topic first to get the topic ID.
        """
        data = await client.post(
            routes.topic_knowledge(topic_id),
            {"title": title, "content": content, "source_type": source_type},
        )
        return [
            TextContent(
                type="text",
                text=(
                    f"Knowledge saved:\n- **{data['title']}**\n"
                    f"- ID: {data['id']}\n"
                    f"- Topic ID: {data['topic_id']}\n"
                    f"- Status: {data['status']}"
                ),
            )
        ]

    @mcp.tool(annotations=write_hint.model_copy(update={"title": "Update Knowledge"}))
    @handle_api_errors
    @track
    async def update_knowledge(
        topic_id: Annotated[str, Field(description="The topic ID the knowledge belongs to.")],
        knowledge_id: Annotated[str, Field(description="The knowledge item ID to update (from get_topic_knowledge).")],
        title: Annotated[str | None, Field(description="Updated title.", default=None)] = None,
        content: Annotated[str | None, Field(description="Updated content.", default=None)] = None,
    ) -> list[TextContent]:
        """Update the title and/or content of an existing knowledge item.

        For text-based items, both title and content can be updated.
        For file-based items (images, PDFs, etc.), only the title can be updated.

        Only call this when the user explicitly asks to update knowledge.
        Always confirm the changes with the user before calling this tool.
        Use get_topic_knowledge first to find the knowledge item ID.
        """
        if title is None and content is None:
            return [TextContent(type="text", text="Nothing to update — provide a title and/or content.")]

        body = {}
        if title is not None:
            body["title"] = title
        if content is not None:
            body["content"] = content

        data = await client.patch(routes.topic_knowledge_detail(topic_id, knowledge_id), body)
        return [
            TextContent(
                type="text",
                text=(f"Knowledge updated:\n- **{data['title']}**\n- ID: {data['id']}\n- Status: {data['status']}"),
            )
        ]
