from __future__ import annotations

import os
import sys
import httpx
from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent, ToolAnnotations

from obris_mcp import routes
from obris_mcp.decorators import APIError, handle_api_errors

load_dotenv()

mcp = FastMCP("obris")
API_BASE = os.environ.get("OBRIS_API_URL", "https://api.obris.ai")
API_KEY = os.environ.get("OBRIS_API_KEY", "")

SOURCE_PARAM = "source"
SOURCE_MCP = "mcp"


async def api_request(path: str, params: dict = None) -> dict:
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            f"{API_BASE}{path}",
            params=params,
            headers={"X-API-Key": API_KEY},
            timeout=30.0,
        )
        if resp.status_code == 401:
            raise APIError(
                "Invalid or missing API key. "
                "Ensure OBRIS_API_KEY is set in your environment."
            )
        if resp.status_code == 403:
            raise APIError(
                "Access denied. "
                "Your API key doesn't have permission for this resource."
            )
        resp.raise_for_status()
        return resp.json()


@mcp.tool(annotations=ToolAnnotations(title="List Topics", readOnlyHint=True, openWorldHint=True))
@handle_api_errors
async def list_topics() -> list[TextContent]:
    """List all topics available to the current user.
    Call this to help the user identify which topic they want to work within.
    """
    data = await api_request(routes.topics())
    topics = data.get("results", [])
    if not topics:
        return [TextContent(type="text", text="No topics found.")]
    results = []
    for t in topics:
        item_count = t.get("item_count", 0)
        desc = f"\n  Description: {t['description']}" if t.get("description") else ""
        results.append(
            f"**{t['name']}**{desc}\n"
            f"- ID: {t['id']}\n"
            f"- Knowledge Count: {item_count}"
        )
    return [TextContent(type="text", text="\n\n".join(results))]


@mcp.tool(annotations=ToolAnnotations(title="Get Topic Knowledge", readOnlyHint=True, openWorldHint=True))
@handle_api_errors
async def get_topic_knowledge(topic_id: str, cursor: str | None = None, client_hint: str | 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.

    Args:
        topic_id: The topic ID (from list_topics).
        cursor: Pagination cursor from a previous call (omit for first page).
        client_hint: Optional. If you know your client, pass it to optimize
            response size. Known values: "claude-code", "claude",
            "chatgpt", "gemini". Omit if unsure.
    """
    params = {SOURCE_PARAM: SOURCE_MCP}
    if client_hint:
        params["client_hint"] = client_hint
    if cursor:
        params["cursor"] = cursor
    data = await api_request(routes.topic_knowledge(topic_id), params=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)

    # Emit download instructions at the top of the first page only.
    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.',
        ))

    return blocks


def main():
    print("Obris MCP server running on stdio", file=sys.stderr)
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()
