"""
Sandbox tools available for execution within the Python sandbox.

These tools are made available to code running in execute_python_code:
- web_search: LLM-synthesized answers from web search
- fetch_as_markdown: Fetch and convert content from URIs to markdown
- upload_file: Upload files to the server
"""

import base64
from typing import Any

from universal_mcp.applications.markitdown.app import MarkitdownApp
from universal_mcp.types import ToolFormat

from universal_mcp.agentr.client import AgentrClient
from universal_mcp.agentr.registry import AgentrRegistry


def create_sandbox_tools(tool_registry: AgentrRegistry | None = None) -> dict[str, Any]:
    """Create tools that are available in the sandbox for code execution"""

    async def web_search(query: str, structured_output: dict | None = None) -> dict:
        """Get an LLM-synthesized answer to a question informed by Exa search results.

        This tool performs a semantic search and uses an LLM to summarize the findings
        into a direct answer or structured data. It is robust and will attempt to
        interpret vague or complex queries constructively.

        **When to use:**
        - ONLY for extremely general, broad knowledge queries, or simple QnA (e.g., "Elon Musk", "Who is the current president of the Venezuala?").
        - Synthesizing facts from multiple sources when no specific domain tool applies (after attempting a search for relevant functions/tools).

        **When NOT to use:**
        - **Specific Domains:** DO NOT use for queries related to specific application/field without searching for more specific functions first using search_functions (e.g.  news search, finance search, maps search). If no relevant functions are available, use this function.
        - **Specific URL Access:** Use `fetch_as_markdown` to read a specific page.
        - **App-Specific Search:** Use dedicated tools (e.g., `reddit__search_reddit`) for platform-specific data.

        Args:
            query (str): The question or topic to answer. Must be a non-empty string.
            structured_output (dict | None): Optional JSON Schema. If provided, the LLM will
                extract data matching this schema.

        Returns:
            dict: A dictionary containing:
                - answer (str | dict | list): The generated text answer. If `structured_output`
                is provided, this will be the parsed JSON data (dict or list).
                - citations (list[dict]): A list of source dictionaries, each containing:
                    - id (str): Unique identifier/URL.
                    - url (str): The source URL.
                    - title (str): Page title.
                    - published_date (str | None): ISO date string if available.
                    - author (str | None): Author name if available.
                    - text (str): A snippet of the content.
        """
        if not tool_registry:
            return {"answer": "Tool registry not provided.", "citations": []}
        await tool_registry.export_tools(["exa__answer"], ToolFormat.LANGCHAIN)

        response = await tool_registry.call_tool(
            "exa__answer", {"query": query, "text": True, "output_schema": structured_output}
        )

        return {
            "answer": response.get("answer"),
            "citations": response.get("citations", []),
        }

    async def fetch_as_markdown(uri: str) -> str:
        """
        Asynchronously extracts main text content from a PUBLIC, STATIC or LOCAL source and converts it to Markdown.

        ### CRITICAL LIMITATIONS (READ BEFORE USING)
        * **DO NOT USE** for Google Docs, Google Sheets, Notion, Jira, Trello, Gmail, Linkedin, etc.
        * **DO NOT USE** for any URL requiring a login, cookies, or OAuth.
        * **DO NOT USE** for dynamic Single Page Applications (SPAs) that require Javascript rendering.

        For such scenarios, search for and use dedicated app-specific functions, or specialized scraping functions as relevant.

        ### WHEN TO USE
        Use this function ONLY for:
        * Public blog posts, articles, and news sites.
        * Wikipedia pages or static documentation.
        * Local files (PDF, PPTX, DOCX, etc.) via `file://`.
        * Raw text/data files (CSV, JSON, XML).


        Args:
            uri (str): The URI pointing to the resource or a local file path.
                    Supported schemes:
                    - http:// or https:// (Public, static web pages only)
                    - file:// (Local or accessible network files)
                    - data: (Embedded data)

        Returns:
            A string containing the markdown representation of the content.

        Raises:
            ValueError: If the URI is invalid, behind a login wall, or unscrapeable.
            Tags:
                convert, markdown, async, uri, transform, document, important
        """
        markitdown = MarkitdownApp()
        response = await markitdown.convert_to_markdown(uri)
        if not response:
            raise ValueError(
                "Failed to convert to markdown. If it is a website, try using application-specific functions or alternate scraping functions."
            )
        return response

    async def upload_file(file_name: str, mime_type: str, base64_data: str) -> dict:
        """
        Uploads a file to the server.

        Args:
            file_name (str): The name of the file to upload.
            mime_type (str): The MIME type of the file.
            base64_data (str): The file content encoded as a base64 string.

        Returns:
            dict: A dictionary containing the result of the upload operation with the following fields:
                - status (str): "success" if the upload succeeded, "error" otherwise.
                - message (str): A message returned by the server, typically indicating success or providing error details.
                - signed_url (str or None): The signed URL to access the uploaded file if successful, None otherwise.
        """
        if not tool_registry:
            return {
                "status": "error",
                "message": "Tool registry not provided.",
                "signed_url": None,
            }

        client: AgentrClient = tool_registry.client
        bytes_data = base64.b64decode(base64_data)
        response = await client._upload_file(file_name, mime_type, bytes_data)
        if response.get("status") != "success":
            return {
                "status": "error",
                "message": response.get("message"),
                "signed_url": None,
            }
        return {
            "status": "success",
            "message": str(response.get("message"))
            + "\n"
            + "Render the uploaded file in Markdown as per instructions to display it to the user.",
            "signed_url": response.get("signed_url"),
        }

    return {
        "web_search": web_search,
        "fetch_as_markdown": fetch_as_markdown,
        "upload_file": upload_file,
    }
