"""Main AgentToolkit class for LLM agent graph exploration."""

from pathlib import Path
from typing import Optional

from emdash.graph.connection import KuzuConnection, get_connection
from emdash.agent.tools.base import BaseTool, ToolResult, ToolCategory
from emdash.agent.session import AgentSession
from emdash.utils.logger import log


class AgentToolkit:
    """Main entry point for LLM agent graph exploration.

    Provides a unified interface for executing graph exploration tools
    and managing exploration session state.

    Example:
        toolkit = AgentToolkit()

        # Search for relevant code
        result = toolkit.search("user authentication")

        # Expand the top result
        if result.success:
            top = result.data["results"][0]
            expanded = toolkit.expand(top["type"], top["qualified_name"])

        # Get OpenAI schemas for function calling
        schemas = toolkit.get_all_schemas()

        # With custom MCP servers
        toolkit = AgentToolkit(mcp_config_path=Path(".emdash/mcp.json"))
    """

    def __init__(
        self,
        connection: Optional[KuzuConnection] = None,
        enable_session: bool = True,
        mcp_config_path: Optional[Path] = None,
    ):
        """Initialize the agent toolkit.

        Args:
            connection: Kuzu connection. If None, uses global connection.
            enable_session: Whether to track exploration state across calls.
            mcp_config_path: Path to MCP config file for dynamic tool registration.
                           If None, checks for .emdash/mcp.json in cwd.
        """
        self.connection = connection or get_connection()
        self.session = AgentSession() if enable_session else None
        self._tools: dict[str, BaseTool] = {}
        self._mcp_manager = None
        self._mcp_config_path = mcp_config_path
        self._register_default_tools()

        # Register dynamic MCP tools from config
        self._init_mcp_manager()

    def _register_default_tools(self) -> None:
        """Register all built-in tools."""
        # Import tools here to avoid circular imports
        from emdash.agent.tools.search import (
            SemanticSearchTool,
            # TextSearchTool,  # Disabled due to DB locking issues
            GrepTool,
        )
        from emdash.agent.tools.web import WebTool
        from emdash.agent.tools.traversal import (
            ExpandNodeTool,
            GetCallersTool,
            GetCalleesTool,
            GetClassHierarchyTool,
            GetFileDependenciesTool,
            GetImpactAnalysisTool,
            GetNeighborsTool,
        )
        from emdash.agent.tools.analytics import (
            GetAreaImportanceTool,
            GetTopPageRankTool,
            GetCommunitiesTool,
            GetCommunityMembersTool,
        )
        # History tools removed - use GitHub MCP tools instead
        from emdash.agent.tools.plan import PlanExplorationTool
        from emdash.agent.tools.github_mcp import (
            GitHubSearchCodeTool,
            GitHubGetFileContentTool,
            GitHubPRDetailsTool,
            GitHubListPRsTool,
            GitHubSearchReposTool,
            GitHubSearchPRsTool,
            GitHubGetIssueTool,
            GitHubViewRepoStructureTool,
            GitHubCreateReviewTool,
        )

        # Register search tools
        self.register_tool(SemanticSearchTool(self.connection))
        # self.register_tool(TextSearchTool(self.connection))  # Disabled due to DB locking issues
        self.register_tool(GrepTool(self.connection))
        self.register_tool(WebTool(self.connection))

        # Register traversal tools
        self.register_tool(ExpandNodeTool(self.connection))
        self.register_tool(GetCallersTool(self.connection))
        self.register_tool(GetCalleesTool(self.connection))
        self.register_tool(GetClassHierarchyTool(self.connection))
        self.register_tool(GetFileDependenciesTool(self.connection))
        self.register_tool(GetImpactAnalysisTool(self.connection))
        self.register_tool(GetNeighborsTool(self.connection))

        # Register analytics tools
        self.register_tool(GetAreaImportanceTool(self.connection))
        self.register_tool(GetTopPageRankTool(self.connection))
        self.register_tool(GetCommunitiesTool(self.connection))
        self.register_tool(GetCommunityMembersTool(self.connection))

        # History/PR tools removed - use GitHub MCP tools instead
        # (github_list_prs, github_pr_details, github_search_prs, etc.)

        # Register planning tool
        self.register_tool(PlanExplorationTool(self.connection))

        # Register GitHub MCP tools (for live GitHub research)
        # These tools require GITHUB_TOKEN and github-mcp-server binary
        self._register_mcp_tools()

        log.debug(f"Registered {len(self._tools)} agent tools")

    def _register_mcp_tools(self) -> None:
        """Register GitHub MCP tools if available.

        MCP tools provide enhanced GitHub research capabilities including
        code search, file content retrieval, and rich PR analysis.
        These tools require:
        - GITHUB_TOKEN or GITHUB_PERSONAL_ACCESS_TOKEN environment variable
        - github-mcp-server binary installed
        """
        from emdash.agent.tools.github_mcp import (
            GitHubSearchCodeTool,
            GitHubGetFileContentTool,
            GitHubPRDetailsTool,
            GitHubListPRsTool,
            GitHubSearchReposTool,
            GitHubSearchPRsTool,
            GitHubGetIssueTool,
            GitHubViewRepoStructureTool,
            GitHubCreateReviewTool,
        )
        from emdash.core.config import get_config

        config = get_config()

        # Only register MCP tools if token is available
        if not config.mcp.is_available:
            log.debug("GitHub MCP tools not registered (no token configured)")
            return

        # Register GitHub MCP tools
        self.register_tool(GitHubSearchCodeTool(self.connection))
        self.register_tool(GitHubGetFileContentTool(self.connection))
        self.register_tool(GitHubPRDetailsTool(self.connection))
        self.register_tool(GitHubListPRsTool(self.connection))
        self.register_tool(GitHubSearchReposTool(self.connection))
        self.register_tool(GitHubSearchPRsTool(self.connection))
        self.register_tool(GitHubGetIssueTool(self.connection))
        self.register_tool(GitHubViewRepoStructureTool(self.connection))
        self.register_tool(GitHubCreateReviewTool(self.connection))

        log.debug("Registered 8 GitHub MCP tools")

    def _init_mcp_manager(self) -> None:
        """Initialize MCP manager and register dynamic tools from config.

        This method loads the MCP configuration file and registers all tools
        from enabled MCP servers. It's called after default tool registration.
        """
        from emdash.agent.mcp import (
            MCPServerManager,
            get_default_mcp_config_path,
            create_tools_from_mcp,
        )

        # Determine config path
        config_path = self._mcp_config_path
        if config_path is None:
            config_path = get_default_mcp_config_path()

        # Only proceed if config file exists
        if not config_path.exists():
            log.debug(f"No MCP config found at {config_path}, skipping dynamic MCP tools")
            return

        try:
            # Create manager
            self._mcp_manager = MCPServerManager(config_path=config_path)

            # Create and register dynamic tools
            tools = create_tools_from_mcp(self._mcp_manager, self.connection)
            for tool in tools:
                # Skip if tool name conflicts with existing tool
                if tool.name in self._tools:
                    log.warning(f"Skipping MCP tool '{tool.name}': conflicts with existing tool")
                    continue
                self.register_tool(tool)

            if tools:
                log.info(f"Registered {len(tools)} dynamic MCP tools from config")

        except Exception as e:
            log.warning(f"Failed to initialize MCP manager: {e}")
            self._mcp_manager = None

    def get_mcp_manager(self):
        """Get the MCP manager instance.

        Returns:
            MCPServerManager or None if not initialized
        """
        return self._mcp_manager

    def register_tool(self, tool: BaseTool) -> None:
        """Register a tool.

        Args:
            tool: Tool instance to register
        """
        self._tools[tool.name] = tool

    def get_tool(self, name: str) -> Optional[BaseTool]:
        """Get a tool by name.

        Args:
            name: Tool name

        Returns:
            Tool instance or None if not found
        """
        return self._tools.get(name)

    def list_tools(self) -> list[dict]:
        """List all available tools.

        Returns:
            List of tool info dicts with name, description, category
        """
        return [
            {
                "name": tool.name,
                "description": tool.description,
                "category": tool.category.value,
            }
            for tool in self._tools.values()
        ]

    def execute(self, tool_name: str, **params) -> ToolResult:
        """Execute a tool by name with parameters.

        Args:
            tool_name: Name of the tool to execute
            **params: Tool-specific parameters

        Returns:
            ToolResult with success/data or error
        """
        tool = self.get_tool(tool_name)

        if not tool:
            return ToolResult.error_result(
                f"Unknown tool: {tool_name}",
                suggestions=[f"Available tools: {list(self._tools.keys())}"],
            )

        try:
            result = tool.execute(**params)

            # Track in session if enabled
            if self.session:
                self.session.record_action(tool_name, params, result)

            return result

        except Exception as e:
            log.exception(f"Tool execution error: {tool_name}")
            return ToolResult.error_result(
                f"Tool execution failed: {str(e)}",
                suggestions=["Check the parameters and try again"],
            )

    def get_all_schemas(self) -> list[dict]:
        """Get OpenAI function calling schemas for all tools.

        Returns:
            List of OpenAI function schemas
        """
        return [tool.get_schema() for tool in self._tools.values()]

    def get_schemas_by_category(self, category: str) -> list[dict]:
        """Get schemas for tools in a specific category.

        Args:
            category: Category name (search, traversal, analytics, history, planning)

        Returns:
            List of OpenAI function schemas for that category
        """
        return [
            tool.get_schema()
            for tool in self._tools.values()
            if tool.category.value == category
        ]

    def get_tools_by_category(self, category: str) -> list[BaseTool]:
        """Get all tools in a category.

        Args:
            category: Category name

        Returns:
            List of tool instances
        """
        return [
            tool
            for tool in self._tools.values()
            if tool.category.value == category
        ]

    def get_session_context(self) -> Optional[dict]:
        """Get current session context summary.

        Returns:
            Session context dict or None if session disabled
        """
        if self.session:
            return self.session.get_context_summary()
        return None

    def get_exploration_steps(self) -> list:
        """Get exploration steps from the current session.

        Returns:
            List of ExplorationStep objects or empty list if session disabled
        """
        if self.session:
            return self.session.steps
        return []

    def reset_session(self) -> None:
        """Reset the exploration session state."""
        if self.session:
            self.session.reset()

    # Convenience methods for common operations

    def search(self, query: str, **kwargs) -> ToolResult:
        """Convenience method for semantic search.

        Args:
            query: Natural language search query
            **kwargs: Additional parameters (entity_types, limit, min_score)

        Returns:
            ToolResult with matching entities
        """
        return self.execute("semantic_search", query=query, **kwargs)

    def expand(
        self,
        node_type: str,
        identifier: str,
        **kwargs,
    ) -> ToolResult:
        """Convenience method for node expansion.

        Args:
            node_type: Type of node (Function, Class, File)
            identifier: Qualified name or file path
            **kwargs: Additional parameters (max_hops)

        Returns:
            ToolResult with expanded graph context
        """
        return self.execute(
            "expand_node",
            node_type=node_type,
            identifier=identifier,
            **kwargs,
        )

    def plan(self, goal: str, **kwargs) -> ToolResult:
        """Convenience method for exploration planning.

        Args:
            goal: What you're trying to understand or accomplish
            **kwargs: Additional parameters (context, constraints, exploration_depth)

        Returns:
            ToolResult with exploration plan
        """
        return self.execute("plan_exploration", goal=goal, **kwargs)
