"""
Base utilities for framework integrations.

Provides common functionality used by all framework-specific integrations.
"""
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional

if TYPE_CHECKING:
    from ..client import AgendexClient

logger = logging.getLogger("agendex.integrations")


class ToolWrapper:
    """
    Wraps a tool function to route through Agendex governance.
    
    Extracts action name, parameters, and handles the governance flow.
    """
    
    def __init__(
        self,
        agendex: AgendexClient,
        task: str,
        tool_name: str,
        tool_func: Callable,
        context_provider: Optional[Callable[[], Dict[str, Any]]] = None,
        on_event: Optional[Callable[[Dict], None]] = None,
    ):
        """
        Initialize tool wrapper.
        
        Args:
            agendex: The AgendexClient instance
            task: Task context for this execution
            tool_name: Name of the tool being wrapped
            tool_func: The original tool function
            context_provider: Optional callable that returns current context (reasoning, etc.)
            on_event: Optional callback for governance events
        """
        self.agendex = agendex
        self.task = task
        self.tool_name = tool_name
        self.tool_func = tool_func
        self.context_provider = context_provider
        self.on_event = on_event
    
    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        """Execute the tool through Agendex governance."""
        action = f"tool.{self.tool_name}"
        
        # Build params from arguments
        params = {"kwargs": kwargs}
        if args:
            params["args"] = args
        
        # Get current context
        context = {}
        if self.context_provider:
            try:
                context = self.context_provider()
            except Exception as e:
                logger.warning(f"Context provider failed: {e}")
        
        # Route through Agendex
        result = self.agendex.invoke(
            action=action,
            params=params,
            task=self.task,
            context=context,
            on_event=self.on_event,
        )
        
        # In shadow mode, execute locally
        if result.get("_agendex") == "shadow":
            return self.tool_func(*args, **kwargs)
        
        # In enforce mode, result comes from /invoke
        return result


class ReasoningAccumulator:
    """
    Accumulates reasoning/thought steps from an agent's execution.
    
    Used to capture the agent's internal reasoning for audit and context.
    """
    
    def __init__(self):
        self.steps: list[Dict[str, Any]] = []
        self.current_thought: Optional[str] = None
    
    def add_step(
        self,
        thought: Optional[str] = None,
        action: Optional[str] = None,
        action_input: Optional[Any] = None,
        observation: Optional[str] = None,
    ) -> None:
        """Add a reasoning step."""
        step = {
            "thought": thought or self.current_thought,
            "action": action,
            "action_input": action_input,
            "observation": observation,
        }
        self.steps.append(step)
        
        if thought:
            self.current_thought = thought
    
    def set_thought(self, thought: str) -> None:
        """Set the current thought (before tool is called)."""
        self.current_thought = thought
    
    def get_context(self) -> Dict[str, Any]:
        """Get the accumulated context for governance."""
        return {
            "reasoning": self.current_thought,
            "reasoning_history": self.steps[-5:] if self.steps else [],  # Last 5 steps
        }
    
    def clear(self) -> None:
        """Clear accumulated reasoning."""
        self.steps = []
        self.current_thought = None


def extract_tool_name(tool: Any) -> str:
    """
    Extract a tool name from various tool representations.
    
    Handles:
    - LangChain Tool objects
    - CrewAI Tool objects
    - Functions with __name__
    - Objects with name attribute
    """
    # Try common patterns
    if hasattr(tool, "name"):
        return tool.name
    
    if hasattr(tool, "__name__"):
        return tool.__name__
    
    if hasattr(tool, "func") and hasattr(tool.func, "__name__"):
        return tool.func.__name__
    
    # Fall back to class name
    return type(tool).__name__


