"""Tools package for Hanzo AI.

This package contains all tools for the Hanzo MCP server. To keep imports
robust across environments (e.g., older Python during CI), heavy imports are
guarded. Submodules can still be imported directly, e.g.:

from hanzo_mcp.tools.llm.llm_tool import LLMTool
"""

# Defer annotation evaluation to avoid import-time NameErrors in constrained envs
from __future__ import annotations

# NOTE: Keep top-level imports resilient for environments without optional deps
try:  # pragma: no cover - import guards for CI environments
    from mcp.server import FastMCP  # type: ignore
except Exception:  # pragma: no cover

    class FastMCP:  # type: ignore
        pass


try:  # pragma: no cover
    from hanzo_mcp.tools.llm import (
        LLMTool,
        ConsensusTool,
        LLMManageTool,
        create_provider_tools,
    )
    from hanzo_mcp.tools.mcp import (
        MCPTool,
        McpAddTool,
        McpStatsTool,
        McpRemoveTool,
    )
    from hanzo_mcp.tools.todo import register_todo_tools
    from hanzo_mcp.tools.agent import register_agent_tools
    from hanzo_mcp.tools.shell import register_shell_tools
    from hanzo_mcp.tools.common import register_batch_tool, register_critic_tool, register_thinking_tool
    from hanzo_mcp.tools.editor import (
        NeovimEditTool,
        NeovimCommandTool,
        NeovimSessionTool,
    )
    from hanzo_mcp.tools.vector import register_vector_tools
    from hanzo_mcp.tools.jupyter import register_jupyter_tools
    from hanzo_mcp.tools.database import DatabaseManager, register_database_tools
    from hanzo_mcp.tools.filesystem import register_filesystem_tools
    from hanzo_mcp.tools.common.base import BaseTool
    from hanzo_mcp.tools.common.mode import activate_mode_from_env
    from hanzo_mcp.tools.common.stats import StatsTool
    from hanzo_mcp.tools.common.tool_list import ToolListTool
    from hanzo_mcp.tools.config.mode_tool import mode_tool
    from hanzo_mcp.tools.common.mode_loader import ModeLoader
    from hanzo_mcp.tools.common.permissions import PermissionManager
    from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
    from hanzo_mcp.tools.common.tool_disable import ToolDisableTool
    from hanzo_mcp.tools.common.plugin_loader import load_user_plugins

    # Try memory tools
    try:
        from hanzo_mcp.tools.memory import register_memory_tools

        MEMORY_TOOLS_AVAILABLE = True
    except Exception:
        MEMORY_TOOLS_AVAILABLE = False
        register_memory_tools = None  # type: ignore
except Exception:
    # Minimal surface to allow submodule imports elsewhere
    # Define stub functions for required imports
    def activate_mode_from_env():
        pass

    class ModeLoader:
        @staticmethod
        def get_enabled_tools_from_mode(base_enabled_tools=None, force_mode=None):
            return base_enabled_tools or {}

        @staticmethod
        def apply_environment_from_mode():
            pass

    def load_user_plugins():
        return {}


# Try to import LSP tool
try:
    from hanzo_mcp.tools.lsp import LSPTool, create_lsp_tool

    LSP_TOOL_AVAILABLE = True
except ImportError:
    LSP_TOOL_AVAILABLE = False

# Try to import refactor tool
try:
    from hanzo_mcp.tools.refactor import RefactorTool, create_refactor_tool

    REFACTOR_TOOL_AVAILABLE = True
except ImportError:
    REFACTOR_TOOL_AVAILABLE = False


def register_all_tools(
    mcp_server: FastMCP,
    permission_manager: PermissionManager,
    agent_model: str | None = None,
    agent_max_tokens: int | None = None,
    agent_api_key: str | None = None,
    agent_base_url: str | None = None,
    agent_max_iterations: int = 10,
    agent_max_tool_uses: int = 30,
    enable_agent_tool: bool = False,
    disable_write_tools: bool = False,
    disable_search_tools: bool = False,
    enabled_tools: dict[str, bool] | None = None,
    vector_config: dict | None = None,
    use_mode: bool = True,
    force_mode: str | None = None,
) -> list[BaseTool]:
    """Register all Hanzo tools with the MCP server.

    Args:
        mcp_server: The FastMCP server instance
        permission_manager: Permission manager for access control
        agent_model: Optional model name for agent tool in LiteLLM format
        agent_max_tokens: Optional maximum tokens for agent responses
        agent_api_key: Optional API key for the LLM provider
        agent_base_url: Optional base URL for the LLM provider API endpoint
        agent_max_iterations: Maximum number of iterations for agent (default: 10)
        agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
        enable_agent_tool: Whether to enable the agent tool (default: False)
        disable_write_tools: Whether to disable write tools (default: False)
        disable_search_tools: Whether to disable search tools (default: False)
        enabled_tools: Dictionary of individual tool enable/disable states (default: None)
        vector_config: Vector store configuration (default: None)
        use_mode: Whether to use mode system for tool configuration (default: True)
        force_mode: Force a specific mode to be active (default: None)

    Returns:
        List of registered BaseTool instances
    """
    # Dictionary to store all registered tools
    all_tools: dict[str, BaseTool] = {}

    # Load user plugins early
    try:
        plugins = load_user_plugins()
        import logging

        logger = logging.getLogger(__name__)
        if plugins:
            logger.info(f"Loaded {len(plugins)} user plugin tools: {', '.join(plugins.keys())}")
    except Exception as e:
        import logging

        logger = logging.getLogger(__name__)
        logger.warning(f"Failed to load user plugins: {e}")
        plugins = {}

    # Apply mode configuration if enabled
    if use_mode:
        # First check for mode activation from environment
        activate_mode_from_env()

        tool_config = ModeLoader.get_enabled_tools_from_mode(base_enabled_tools=enabled_tools, force_mode=force_mode)
        # Apply mode environment variables
        ModeLoader.apply_environment_from_mode()
    else:
        # Use individual tool configuration if provided, otherwise fall back to category-level flags
        tool_config = enabled_tools or {}

    def is_tool_enabled(tool_name: str, category_enabled: bool = True) -> bool:
        """Check if a specific tool should be enabled."""
        if tool_name in tool_config:
            return tool_config[tool_name]
        return category_enabled

    # Register filesystem tools with individual configuration
    filesystem_enabled = {
        "read": is_tool_enabled("read", True),
        "write": is_tool_enabled("write", not disable_write_tools),
        "edit": is_tool_enabled("edit", not disable_write_tools),
        "multi_edit": is_tool_enabled("multi_edit", not disable_write_tools),
        "directory_tree": is_tool_enabled("directory_tree", True),
        "ast": is_tool_enabled("ast", not disable_search_tools),
        "rules": is_tool_enabled("rules", True),
        "search": is_tool_enabled("search", not disable_search_tools),
        "find": is_tool_enabled("find", True),  # Fast file finder
    }

    # Vector tools setup (needed for search)
    project_manager = None
    vector_enabled = {
        "vector_index": is_tool_enabled("vector_index", False),
        "vector_search": is_tool_enabled("vector_search", False),
    }

    # Create project manager if vector tools, batch_search, or search are enabled
    if (
        any(vector_enabled.values())
        or filesystem_enabled.get("batch_search", False)
        or filesystem_enabled.get("search", False)
    ):
        if vector_config:
            from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager

            search_paths = [str(path) for path in permission_manager.allowed_paths]
            project_manager = ProjectVectorManager(
                global_db_path=vector_config.get("data_path"),
                embedding_model=vector_config.get("embedding_model", "text-embedding-3-small"),
                dimension=vector_config.get("dimension", 1536),
            )
            # Auto-detect projects from search paths
            detected_projects = project_manager.detect_projects(search_paths)
            import logging

            logger = logging.getLogger(__name__)
            logger.info(f"Detected {len(detected_projects)} projects with LLM.md files")

    filesystem_tools = register_filesystem_tools(
        mcp_server,
        permission_manager,
        enabled_tools=filesystem_enabled,
        project_manager=project_manager,
    )
    for tool in filesystem_tools:
        all_tools[tool.name] = tool

    # Register jupyter tools if enabled
    jupyter_enabled = {
        "jupyter": is_tool_enabled("jupyter", True),  # Unified tool
        "notebook_read": is_tool_enabled("notebook_read", True),  # Legacy support
        "notebook_edit": is_tool_enabled("notebook_edit", True),  # Legacy support
    }

    if any(jupyter_enabled.values()):
        jupyter_tools = register_jupyter_tools(mcp_server, permission_manager, enabled_tools=jupyter_enabled)
        for tool in jupyter_tools:
            all_tools[tool.name] = tool

    # Register shell tools if enabled
    if is_tool_enabled("run_command", True):
        shell_tools = register_shell_tools(mcp_server, permission_manager)
        for tool in shell_tools:
            all_tools[tool.name] = tool

    # Register agent tools if enabled
    agent_enabled = enable_agent_tool or is_tool_enabled("agent", False) or is_tool_enabled("dispatch_agent", False)
    swarm_enabled = is_tool_enabled("swarm", False)

    if agent_enabled or swarm_enabled:
        agent_tools = register_agent_tools(
            mcp_server,
            permission_manager,
            agent_model=agent_model,
            agent_max_tokens=agent_max_tokens,
            agent_api_key=agent_api_key,
            agent_base_url=agent_base_url,
            agent_max_iterations=agent_max_iterations,
            agent_max_tool_uses=agent_max_tool_uses,
        )
        # Filter based on what's enabled
        for tool in agent_tools:
            if tool.name == "agent" and agent_enabled or tool.name == "swarm" and swarm_enabled:
                all_tools[tool.name] = tool
            elif tool.name in ["claude", "codex", "gemini", "grok", "code_auth"]:
                # CLI tools and auth are always included when agent tools are enabled
                all_tools[tool.name] = tool

    # Register todo tools if enabled
    todo_enabled = {
        "todo": is_tool_enabled("todo", True),
        # Backward compatibility - if old names are used, enable the unified tool
        "todo_read": is_tool_enabled("todo_read", True),
        "todo_write": is_tool_enabled("todo_write", True),
    }

    # Enable unified todo if any of the todo tools are enabled
    if any(todo_enabled.values()):
        todo_tools = register_todo_tools(mcp_server, enabled_tools={"todo": True})
        for tool in todo_tools:
            all_tools[tool.name] = tool

    # Register thinking tool if enabled
    if is_tool_enabled("think", True):
        thinking_tool = register_thinking_tool(mcp_server)
        for tool in thinking_tool:
            all_tools[tool.name] = tool

    # Register critic tool if enabled
    if is_tool_enabled("critic", True):
        critic_tools = register_critic_tool(mcp_server)
        for tool in critic_tools:
            all_tools[tool.name] = tool

    # Register vector tools if enabled (reuse project_manager if available)
    if any(vector_enabled.values()) and project_manager:
        vector_tools = register_vector_tools(
            mcp_server,
            permission_manager,
            vector_config=vector_config,
            enabled_tools=vector_enabled,
            project_manager=project_manager,
        )
        for tool in vector_tools:
            all_tools[tool.name] = tool

    # Register batch tool if enabled (batch tool is typically always enabled)
    if is_tool_enabled("batch", True):
        register_batch_tool(mcp_server, all_tools)

    # Register database tools if enabled
    db_manager = None
    database_enabled = {
        "sql_query": is_tool_enabled("sql_query", True),
        "sql_search": is_tool_enabled("sql_search", True),
        "sql_stats": is_tool_enabled("sql_stats", True),
        "graph_add": is_tool_enabled("graph_add", True),
        "graph_remove": is_tool_enabled("graph_remove", True),
        "graph_query": is_tool_enabled("graph_query", True),
        "graph_search": is_tool_enabled("graph_search", True),
        "graph_stats": is_tool_enabled("graph_stats", True),
    }

    if any(database_enabled.values()):
        db_manager = DatabaseManager(permission_manager)
        database_tools = register_database_tools(
            mcp_server,
            permission_manager,
            db_manager=db_manager,
        )
        # Filter based on enabled state
        for tool in database_tools:
            if database_enabled.get(tool.name, True):
                all_tools[tool.name] = tool

    # Register unified MCP tool if enabled
    if is_tool_enabled("mcp", True):
        tool = MCPTool()
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    # Register legacy MCP tools if explicitly enabled (disabled by default)
    legacy_mcp_enabled = {
        "mcp_add": is_tool_enabled("mcp_add", False),
        "mcp_remove": is_tool_enabled("mcp_remove", False),
        "mcp_stats": is_tool_enabled("mcp_stats", False),
    }

    if legacy_mcp_enabled.get("mcp_add", False):
        tool = McpAddTool()
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    if legacy_mcp_enabled.get("mcp_remove", False):
        tool = McpRemoveTool()
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    if legacy_mcp_enabled.get("mcp_stats", False):
        tool = McpStatsTool()
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    # Register system tools (always enabled)
    # Tool enable/disable tools
    tool_enable = ToolEnableTool()
    tool_enable.register(mcp_server)
    all_tools[tool_enable.name] = tool_enable

    tool_disable = ToolDisableTool()
    tool_disable.register(mcp_server)
    all_tools[tool_disable.name] = tool_disable

    tool_list = ToolListTool()
    tool_list.register(mcp_server)
    all_tools[tool_list.name] = tool_list

    # Stats tool
    stats_tool = StatsTool(db_manager=db_manager)
    stats_tool.register(mcp_server)
    all_tools[stats_tool.name] = stats_tool

    # Mode tool (always enabled for managing tool sets)
    mode_tool.register(mcp_server)
    all_tools[mode_tool.name] = mode_tool

    # Register editor tools if enabled
    editor_enabled = {
        "neovim_edit": is_tool_enabled("neovim_edit", True),
        "neovim_command": is_tool_enabled("neovim_command", True),
        "neovim_session": is_tool_enabled("neovim_session", True),
    }

    if editor_enabled.get("neovim_edit", True):
        tool = NeovimEditTool(permission_manager)
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    if editor_enabled.get("neovim_command", True):
        tool = NeovimCommandTool(permission_manager)
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    if editor_enabled.get("neovim_session", True):
        tool = NeovimSessionTool()
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    # Register unified LLM tool if enabled
    if is_tool_enabled("llm", True):
        tool = LLMTool()
        if tool.available_providers:  # Only register if API keys found
            tool.register(mcp_server)
            all_tools[tool.name] = tool

    # Register consensus tool if enabled (enabled by default)
    if is_tool_enabled("consensus", True):
        tool = ConsensusTool()
        if tool.llm_tool.available_providers:
            tool.register(mcp_server)
            all_tools[tool.name] = tool

    # Register legacy LLM tools if explicitly enabled (disabled by default)
    legacy_llm_enabled = {
        "llm_legacy": is_tool_enabled("llm_legacy", False),
        "consensus": is_tool_enabled("consensus", False),
        "llm_manage": is_tool_enabled("llm_manage", False),
    }

    if legacy_llm_enabled.get("llm_legacy", False):
        tool = LLMTool()
        if tool.available_providers:
            tool.register(mcp_server)
            all_tools["llm_legacy"] = tool

    if legacy_llm_enabled.get("consensus", False):
        tool = ConsensusTool()
        if tool.llm_tool.available_providers:
            tool.register(mcp_server)
            all_tools[tool.name] = tool

    if legacy_llm_enabled.get("llm_manage", False):
        tool = LLMManageTool()
        tool.register(mcp_server)
        all_tools[tool.name] = tool

    # Register provider-specific LLM tools (disabled by default)
    if is_tool_enabled("provider_specific_llm", False):
        provider_tools = create_provider_tools()
        for tool in provider_tools:
            if is_tool_enabled(tool.name, False):
                tool.register(mcp_server)
                all_tools[tool.name] = tool

    # Register memory tools if enabled
    memory_enabled = {
        "recall_memories": is_tool_enabled("recall_memories", True),
        "create_memories": is_tool_enabled("create_memories", True),
        "update_memories": is_tool_enabled("update_memories", True),
        "delete_memories": is_tool_enabled("delete_memories", True),
        "manage_memories": is_tool_enabled("manage_memories", True),
        "recall_facts": is_tool_enabled("recall_facts", True),
        "store_facts": is_tool_enabled("store_facts", True),
        "summarize_to_memory": is_tool_enabled("summarize_to_memory", True),
        "manage_knowledge_bases": is_tool_enabled("manage_knowledge_bases", True),
    }

    if any(memory_enabled.values()) and MEMORY_TOOLS_AVAILABLE:
        try:
            memory_tools = register_memory_tools(
                mcp_server, permission_manager, user_id="default", project_id="default"
            )
            # Filter based on enabled state
            for tool in memory_tools:
                if memory_enabled.get(tool.name, True):
                    all_tools[tool.name] = tool
        except Exception as e:
            logger.warning(f"Failed to register memory tools: {e}")

    # Register LSP tool if enabled
    if is_tool_enabled("lsp", True) and LSP_TOOL_AVAILABLE:
        try:
            tool = create_lsp_tool()
            tool.register(mcp_server)
            all_tools[tool.name] = tool
        except Exception as e:
            logger.warning(f"Failed to register LSP tool: {e}")

    # Register refactor tool if enabled
    if is_tool_enabled("refactor", True) and REFACTOR_TOOL_AVAILABLE:
        try:
            tool = create_refactor_tool()
            tool.register(mcp_server)
            all_tools[tool.name] = tool
        except Exception as e:
            logger.warning(f"Failed to register refactor tool: {e}")

    # Register user plugins last (so they can override built-in tools)
    for plugin_name, plugin in plugins.items():
        if is_tool_enabled(plugin_name, True):
            try:
                tool = plugin.tool_class()
                tool.register(mcp_server)
                all_tools[tool.name] = tool
                logger.info(f"Registered plugin tool: {plugin_name}")
            except Exception as e:
                logger.error(f"Failed to register plugin tool {plugin_name}: {e}")

    # Return all registered tools
    return list(all_tools.values())
