"""
Google ADK adapter for Boring-OS.

Provides Google ADK-compatible tools that wrap Boring-OS tool calls.

Usage:
    from boring_os import BoringOS
    from boring_os.adapters.adk import BoringOSToolProvider
    
    client = BoringOS(api_key="...")
    provider = BoringOSToolProvider(client)
    
    # Use with ADK agent
    from google.adk import Agent
    agent = Agent(tools=provider.get_tools())

Requires: pip install boring-os[adk]
"""

from __future__ import annotations

import asyncio
from typing import TYPE_CHECKING, Any, Callable

# Lazy import to avoid requiring google-adk as a hard dependency
try:
    from google.adk.tools import FunctionTool, ToolContext
    
    ADK_AVAILABLE = True
except ImportError:
    ADK_AVAILABLE = False
    FunctionTool = object  # type: ignore
    ToolContext = Any  # type: ignore

if TYPE_CHECKING:
    from boring_os.client import BoringOS
    from boring_os.models import Tool, ToolInfo


def _check_adk_available() -> None:
    """Raise ImportError if google-adk is not installed."""
    if not ADK_AVAILABLE:
        raise ImportError(
            "Google ADK is required for this adapter. "
            "Install with: pip install boring-os[adk]"
        )


def _create_tool_function(
    client: "BoringOS",
    tool_name: str,
) -> Callable[..., dict[str, Any]]:
    """
    Create a callable function for ADK tool execution.
    
    Args:
        client: Boring-OS client
        tool_name: Name of the tool to wrap
    
    Returns:
        Callable that executes the tool
    """
    def tool_func(**kwargs: Any) -> dict[str, Any]:
        """Execute the Boring-OS tool."""
        return client.tools.call(tool_name, **kwargs)
    
    return tool_func


async def _create_async_tool_function(
    client: "BoringOS",
    tool_name: str,
) -> Callable[..., Any]:
    """
    Create an async callable function for ADK tool execution.
    
    Args:
        client: Boring-OS client
        tool_name: Name of the tool to wrap
    
    Returns:
        Async callable that executes the tool
    """
    async def tool_func(**kwargs: Any) -> dict[str, Any]:
        """Execute the Boring-OS tool asynchronously."""
        return await client.tools.call_async(tool_name, **kwargs)
    
    return tool_func


class BoringOSToolProvider:
    """
    Google ADK tool provider for Boring-OS tools.
    
    Fetches tools from Boring-OS and provides them in ADK-compatible format.
    
    Example:
        from boring_os import BoringOS
        from boring_os.adapters.adk import BoringOSToolProvider
        
        client = BoringOS(api_key="...")
        provider = BoringOSToolProvider(client)
        
        # Get all tools
        tools = provider.get_tools()
        
        # Get specific tools
        tools = provider.get_tools(tool_names=["get_customer_profile"])
    """
    
    def __init__(self, client: "BoringOS"):
        """
        Initialize the tool provider.
        
        Args:
            client: Boring-OS client instance
        """
        _check_adk_available()
        self._client = client
        self._tools_cache: dict[str, Any] = {}
    
    def _build_function_schema(self, tool: "Tool") -> dict[str, Any]:
        """
        Build ADK function schema from Boring-OS tool definition.
        
        Args:
            tool: Tool definition
        
        Returns:
            ADK-compatible function schema
        """
        # Extract input schema properties
        input_schema = tool.schema_.input if tool.schema_ else {}
        properties = input_schema.get("properties", {})
        required = input_schema.get("required", [])
        
        # Build parameter definitions
        parameters: dict[str, Any] = {
            "type": "object",
            "properties": {},
            "required": required,
        }
        
        for name, prop in properties.items():
            param_def: dict[str, Any] = {
                "type": prop.get("type", "string"),
            }
            if "description" in prop:
                param_def["description"] = prop["description"]
            if "enum" in prop:
                param_def["enum"] = prop["enum"]
            if "default" in prop:
                param_def["default"] = prop["default"]
            
            parameters["properties"][name] = param_def
        
        return parameters
    
    def _create_adk_tool(self, tool: "Tool") -> Any:
        """
        Create an ADK FunctionTool from a Boring-OS tool.
        
        Args:
            tool: Tool definition
        
        Returns:
            ADK FunctionTool instance
        """
        # Build description
        description = tool.description or f"Execute the {tool.name} tool"
        if tool.instructions:
            if tool.instructions.summary:
                description = tool.instructions.summary
            if tool.instructions.when_to_use:
                description += f" Use when: {tool.instructions.when_to_use}"
        
        # Create the tool function
        tool_func = _create_tool_function(self._client, tool.name)
        
        # Set function metadata for ADK
        tool_func.__name__ = tool.name
        tool_func.__doc__ = description
        
        # Create FunctionTool with schema
        schema = self._build_function_schema(tool)
        
        return FunctionTool(
            func=tool_func,
            name=tool.name,
            description=description,
            parameters=schema,
        )
    
    def get_tools(
        self,
        tool_names: list[str] | None = None,
        refresh: bool = False,
    ) -> list[Any]:
        """
        Get ADK-compatible tools from Boring-OS.
        
        Args:
            tool_names: Optional list of specific tools to include
            refresh: If True, refresh the tools cache
        
        Returns:
            List of ADK FunctionTool instances
        """
        if refresh:
            self._tools_cache.clear()
        
        tools: list[Any] = []
        
        if tool_names:
            # Fetch specific tools
            for name in tool_names:
                if name in self._tools_cache:
                    tools.append(self._tools_cache[name])
                else:
                    try:
                        tool_def = self._client.tools.get(name)
                        adk_tool = self._create_adk_tool(tool_def)
                        self._tools_cache[name] = adk_tool
                        tools.append(adk_tool)
                    except Exception:
                        pass
        else:
            # Fetch all active tools
            from boring_os.models import ToolStatus
            
            response = self._client.tools.list(status=ToolStatus.ACTIVE, limit=1000)
            
            for tool_info in response.tools:
                if tool_info.name in self._tools_cache:
                    tools.append(self._tools_cache[tool_info.name])
                else:
                    try:
                        tool_def = self._client.tools.get(tool_info.name)
                        adk_tool = self._create_adk_tool(tool_def)
                        self._tools_cache[tool_info.name] = adk_tool
                        tools.append(adk_tool)
                    except Exception:
                        pass
        
        return tools
    
    def get_tool(self, tool_name: str) -> Any:
        """
        Get a single ADK tool by name.
        
        Args:
            tool_name: Name of the tool
        
        Returns:
            ADK FunctionTool instance
        
        Raises:
            ToolNotFoundError: If tool doesn't exist
        """
        if tool_name in self._tools_cache:
            return self._tools_cache[tool_name]
        
        tool_def = self._client.tools.get(tool_name)
        adk_tool = self._create_adk_tool(tool_def)
        self._tools_cache[tool_name] = adk_tool
        return adk_tool


def get_adk_tools(
    client: "BoringOS",
    tool_names: list[str] | None = None,
) -> list[Any]:
    """
    Convenience function to get ADK tools from Boring-OS.
    
    Args:
        client: Boring-OS client instance
        tool_names: Optional list of specific tools
    
    Returns:
        List of ADK FunctionTool instances
    
    Example:
        from boring_os import BoringOS
        from boring_os.adapters.adk import get_adk_tools
        
        client = BoringOS(api_key="...")
        tools = get_adk_tools(client)
    """
    provider = BoringOSToolProvider(client)
    return provider.get_tools(tool_names)
