"""
Base Hook Classes

Abstract base classes for pre-tool and post-tool hooks.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from enum import Enum
from typing import Any, Optional


class HookAction(Enum):
    """Action to take after hook execution."""
    
    ALLOW = "allow"      # Allow the tool call to proceed
    MODIFY = "modify"    # Allow but modify the arguments
    BLOCK = "block"      # Block the tool call
    WARN = "warn"        # Warn but allow


@dataclass
class HookResult:
    """Result from a hook execution."""
    
    action: HookAction
    message: Optional[str] = None
    modified_args: Optional[dict[str, Any]] = None

    @classmethod
    def allow(cls) -> "HookResult":
        """Allow the tool call."""
        return cls(action=HookAction.ALLOW)

    @classmethod
    def block(cls, message: str) -> "HookResult":
        """Block the tool call."""
        return cls(action=HookAction.BLOCK, message=message)

    @classmethod
    def warn(cls, message: str) -> "HookResult":
        """Warn but allow."""
        return cls(action=HookAction.WARN, message=message)

    @classmethod
    def modify(cls, modified_args: dict[str, Any], message: str = None) -> "HookResult":
        """Modify the arguments."""
        return cls(action=HookAction.MODIFY, modified_args=modified_args, message=message)


class Hook(ABC):
    """Abstract base class for all hooks."""
    
    @property
    @abstractmethod
    def name(self) -> str:
        """Name of the hook."""
        pass

    @property
    @abstractmethod
    def description(self) -> str:
        """Description of what the hook does."""
        pass


class PreToolHook(Hook):
    """
    Hook that runs before a tool is executed.
    
    Can:
    - Block the tool call
    - Modify the arguments
    - Warn about potential issues
    - Allow the call to proceed
    """
    
    @abstractmethod
    def before_execute(
        self,
        tool_name: str,
        args: dict[str, Any],
    ) -> HookResult:
        """
        Called before a tool is executed.
        
        Args:
            tool_name: Name of the tool being called
            args: Arguments being passed to the tool
            
        Returns:
            HookResult indicating action to take
        """
        pass

    def applies_to(self, tool_name: str) -> bool:
        """
        Check if this hook applies to the given tool.
        
        Override to filter which tools this hook applies to.
        Default: applies to all tools.
        """
        return True


class PostToolHook(Hook):
    """
    Hook that runs after a tool is executed.
    
    Can:
    - Log the result
    - Modify the output
    - Trigger additional actions
    """
    
    @abstractmethod
    def after_execute(
        self,
        tool_name: str,
        args: dict[str, Any],
        result: Any,
    ) -> Any:
        """
        Called after a tool is executed.
        
        Args:
            tool_name: Name of the tool that was called
            args: Arguments that were passed
            result: Result from the tool execution
            
        Returns:
            Potentially modified result
        """
        pass


class HookRegistry:
    """Registry for managing hooks."""
    
    def __init__(self):
        self.pre_hooks: list[PreToolHook] = []
        self.post_hooks: list[PostToolHook] = []

    def register_pre_hook(self, hook: PreToolHook) -> None:
        """Register a pre-tool hook."""
        self.pre_hooks.append(hook)

    def register_post_hook(self, hook: PostToolHook) -> None:
        """Register a post-tool hook."""
        self.post_hooks.append(hook)

    def run_pre_hooks(
        self,
        tool_name: str,
        args: dict[str, Any],
    ) -> tuple[HookResult, dict[str, Any]]:
        """
        Run all applicable pre-hooks.
        
        Returns:
            Tuple of (final result, potentially modified args)
        """
        current_args = args.copy()
        
        for hook in self.pre_hooks:
            if not hook.applies_to(tool_name):
                continue
            
            result = hook.before_execute(tool_name, current_args)
            
            if result.action == HookAction.BLOCK:
                return result, current_args
            
            if result.action == HookAction.MODIFY and result.modified_args:
                current_args.update(result.modified_args)
        
        return HookResult.allow(), current_args

    def run_post_hooks(
        self,
        tool_name: str,
        args: dict[str, Any],
        result: Any,
    ) -> Any:
        """Run all post-hooks and return potentially modified result."""
        current_result = result
        
        for hook in self.post_hooks:
            current_result = hook.after_execute(tool_name, args, current_result)
        
        return current_result
