# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Toolbox Registry for the Agent service.
Manages thematic tool groupings (toolboxes) and the `requestToolbox` meta-tool.
"""

import logging
from typing import Dict, List, Any, Optional, Set
from pydantic import BaseModel, Field

logger = logging.getLogger(__name__)


class ToolboxDefinition(BaseModel):
    """Definition of a thematic toolbox."""
    id: str = Field(description="Unique toolbox identifier (e.g. 'core', 'email', 'workflow')")
    label: str = Field(description="Human-readable label")
    description: str = Field(default="", description="What this toolbox provides")
    featureCode: Optional[str] = Field(default=None, description="Feature code if toolbox is feature-specific")
    tools: List[str] = Field(default_factory=list, description="Tool names belonging to this toolbox")
    isDefault: bool = Field(default=False, description="If true, toolbox is active by default")
    requiresConnection: Optional[str] = Field(
        default=None,
        description="Connection authority required (e.g. 'microsoft', 'google'). None = always available."
    )


class ToolboxRegistry:
    """Registry for toolbox definitions. Manages activation and tool lookup."""

    def __init__(self):
        self._toolboxes: Dict[str, ToolboxDefinition] = {}

    def registerToolbox(self, toolbox: ToolboxDefinition) -> None:
        """Register a toolbox definition."""
        if toolbox.id in self._toolboxes:
            logger.debug("Toolbox '%s' already registered, updating", toolbox.id)
        self._toolboxes[toolbox.id] = toolbox
        logger.debug("Registered toolbox: %s (%d tools, default=%s)", toolbox.id, len(toolbox.tools), toolbox.isDefault)

    def getToolbox(self, toolboxId: str) -> Optional[ToolboxDefinition]:
        """Get a toolbox by ID."""
        return self._toolboxes.get(toolboxId)

    def getAllToolboxes(self) -> List[ToolboxDefinition]:
        """Get all registered toolboxes."""
        return list(self._toolboxes.values())

    def getDefaultToolboxes(self) -> List[ToolboxDefinition]:
        """Get all default toolboxes (active at agent start)."""
        return [tb for tb in self._toolboxes.values() if tb.isDefault]

    def getActiveToolboxes(self, userConnections: List[str] = None) -> List[ToolboxDefinition]:
        """
        Get toolboxes available to the user based on their connections.
        Toolboxes without requiresConnection are always available.
        Toolboxes with requiresConnection are available only if the user has that connection.
        """
        available = []
        connectionAuthorities: Set[str] = set(userConnections or [])
        for tb in self._toolboxes.values():
            if tb.requiresConnection is None:
                available.append(tb)
            elif tb.requiresConnection in connectionAuthorities:
                available.append(tb)
        return available

    def getToolsForToolboxes(self, toolboxIds: List[str]) -> List[str]:
        """Get the union of all tool names for the given toolbox IDs."""
        tools: Set[str] = set()
        for tbId in toolboxIds:
            tb = self._toolboxes.get(tbId)
            if tb:
                tools.update(tb.tools)
        return sorted(tools)

    def getToolboxForTool(self, toolName: str) -> Optional[str]:
        """Find which toolbox a tool belongs to."""
        for tb in self._toolboxes.values():
            if toolName in tb.tools:
                return tb.id
        return None

    def toApiResponse(self, userConnections: List[str] = None) -> List[Dict[str, Any]]:
        """Serialize available toolboxes for API response."""
        available = self.getActiveToolboxes(userConnections)
        return [
            {
                "id": tb.id,
                "label": tb.label,
                "description": tb.description,
                "toolCount": len(tb.tools),
                "isDefault": tb.isDefault,
                "requiresConnection": tb.requiresConnection,
            }
            for tb in available
        ]


# Module-level singleton
_toolboxRegistry = ToolboxRegistry()


def getToolboxRegistry() -> ToolboxRegistry:
    """Get the global toolbox registry singleton."""
    return _toolboxRegistry


def _registerDefaultToolboxes() -> None:
    """Register the default set of toolboxes."""
    defaults = [
        ToolboxDefinition(
            id="core",
            label="Core Tools",
            description="Basic agent tools: search, read, write, web",
            isDefault=True,
            tools=[
                "readFile", "listFiles", "searchInFileContent", "listFolders",
                "webSearch", "readUrl", "writeFile", "deleteFile", "renameFile",
                "copyFile", "createFolder", "deleteFolder", "moveFile", "moveFolder",
                "renameFolder", "tagFile", "replaceInFile", "translateText",
                "detectLanguage", "queryFeatureInstance",
            ],
        ),
        ToolboxDefinition(
            id="ai",
            label="AI Tools",
            description="AI-powered analysis and generation",
            isDefault=True,
            tools=[
                "summarizeContent", "describeImage", "generateImage",
                "textToSpeech", "speechToText", "renderDocument",
                "createChart", "executeCode", "neutralizeData",
            ],
        ),
        ToolboxDefinition(
            id="datasources",
            label="Data Sources",
            description="Access external data sources and databases",
            isDefault=True,
            tools=[
                "listConnections", "browseDataSource", "searchDataSource",
                "downloadFromDataSource", "uploadToExternal",
                "browseContainer", "readContentObjects", "extractContainerItem",
            ],
        ),
        ToolboxDefinition(
            id="email",
            label="Email",
            description="Send emails or save as draft via Outlook (supports HTML body and file attachments). Use sendMail with draft=true for drafts.",
            requiresConnection="microsoft",
            isDefault=False,
            tools=[
                "sendMail",
            ],
        ),
        ToolboxDefinition(
            id="sharepoint",
            label="SharePoint",
            description="Access SharePoint sites, lists, and files",
            requiresConnection="microsoft",
            isDefault=False,
            tools=[
                "sharepoint_findDocuments", "sharepoint_readDocuments",
                "sharepoint_upload",
            ],
        ),
        ToolboxDefinition(
            id="clickup",
            label="ClickUp",
            description="Manage ClickUp tasks and projects",
            requiresConnection="clickup",
            isDefault=False,
            tools=[
                "clickup_searchTasks", "clickup_createTask", "clickup_updateTask",
            ],
        ),
        ToolboxDefinition(
            id="jira",
            label="Jira",
            description="Manage Jira issues and projects",
            requiresConnection="jira",
            isDefault=False,
            tools=[
                "jira_connect", "jira_exportTickets", "jira_importTickets",
            ],
        ),
        ToolboxDefinition(
            id="workflow",
            label="Workflow",
            description="Graph manipulation tools for the visual editor",
            featureCode="graphicalEditor",
            isDefault=False,
            tools=[
                "readWorkflowGraph", "addNode", "removeNode", "connectNodes",
                "setNodeParameter", "listAvailableNodeTypes", "validateGraph",
                "listWorkflowHistory", "readWorkflowMessages",
            ],
        ),
        ToolboxDefinition(
            id="trustee",
            label="Trustee / Accounting",
            description="Trustee accounting tools: refresh data from external system (e.g. Abacus), query positions and journal entries",
            featureCode="trustee",
            isDefault=False,
            tools=[
                "trustee_refreshAccountingData",
            ],
        ),
    ]
    for tb in defaults:
        _toolboxRegistry.registerToolbox(tb)


_registerDefaultToolboxes()


REQUEST_TOOLBOX_TOOL_NAME = "requestToolbox"


def buildRequestToolboxDefinition(availableToolboxIds: List[str]) -> dict:
    """Build the tool definition dict for the requestToolbox meta-tool."""
    return {
        "name": REQUEST_TOOLBOX_TOOL_NAME,
        "description": (
            "Request additional specialized tools for the current task. "
            "Call this when you need tools from a specific toolbox that is not yet active. "
            "After calling, the requested tools will be available in the next round."
        ),
        "parameters": {
            "type": "object",
            "properties": {
                "toolboxId": {
                    "type": "string",
                    "enum": availableToolboxIds,
                    "description": "ID of the toolbox to activate",
                },
                "reason": {
                    "type": "string",
                    "description": "Brief reason why this toolbox is needed",
                },
            },
            "required": ["toolboxId"],
        },
    }
