"""
CQ-AI Hook System

Manages automated triggers that run before or after tool execution.
Inspired by Claude Code hooks.
"""

import subprocess
import re
from pathlib import Path
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from rich.console import Console

console = Console()

@dataclass
class HookConfig:
    matcher: str
    commands: List[str]

class HookSystem:
    def __init__(self, project_root: Path):
        self.project_root = project_root
        self.pre_tool_hooks: List[HookConfig] = []
        self.post_tool_hooks: List[HookConfig] = []
        self.stop_hooks: List[HookConfig] = []
        
    def load_hooks(self, config: Dict[str, Any]):
        """Load hooks from dictionary configuration."""
        for hook_data in config.get("PreToolUse", []):
            self.pre_tool_hooks.append(HookConfig(hook_data["matcher"], hook_data["hooks"]))
            
        for hook_data in config.get("PostToolUse", []):
            self.post_tool_hooks.append(HookConfig(hook_data["matcher"], hook_data["hooks"]))
            
        for hook_data in config.get("Stop", []):
            self.stop_hooks.append(HookConfig(hook_data["matcher"], hook_data["hooks"]))

    def run_pre_tool_hooks(self, tool_name: str, tool_input: Dict[str, Any]) -> bool:
        """Run pre-tool hooks. Return False if execution should be blocked."""
        # Convert input to string for matching
        input_str = str(tool_input)
        
        for hook in self.pre_tool_hooks:
            if self._matches(hook.matcher, tool_name, input_str):
                for cmd in hook.commands:
                    console.print(f"[dim]Running Pre-Hook: {cmd}[/dim]")
                    if not self._run_command(cmd):
                        return False # Block execution if hook fails? Or just warn?
                        # Claude Code blocks if user says so, but here we assume hooks are helpers.
                        # Let's create a special return based on hook result?
                        # For now, just run.
        return True

    def run_post_tool_hooks(self, tool_name: str, tool_result: str) -> Optional[str]:
        """Run post-tool hooks. Return output to append to tool result."""
        feedback = []
        
        for hook in self.post_tool_hooks:
            # Match against tool name only for now, or check file extension in result?
            # Claude Code matcher "Edit && .ts" implies looking at arguments.
            # We need tool_input here too ideally.
            if self._matches(hook.matcher, tool_name, tool_result):
                 for cmd in hook.commands:
                    console.print(f"[dim]Running Post-Hook: {cmd}[/dim]")
                    success, output = self._run_command_capture(cmd)
                    if not success:
                        feedback.append(f"Hook '{cmd}' failed:\n{output}")
                    elif output.strip():
                        # If output exists (like linter warnings), return it
                        feedback.append(f"Hook '{cmd}' output:\n{output}")
                        
        return "\n".join(feedback) if feedback else None

    def _matches(self, matcher: str, tool_name: str, content: str) -> bool:
        """Check if hook matches context."""
        # Simple implementation: check if matcher strings are in content
        # "Edit && .ts" -> tool_name == Edit AND .ts in content
        
        conditions = [c.strip() for c in matcher.split("&&")]
        for cond in conditions:
            # Special tool name check
            if cond in ["Edit", "Read", "Write", "Bash"]:
                if tool_name != cond and tool_name != f"{cond}File": # Handle mapping
                     return False
            # Extension check
            elif cond.startswith("."):
                if cond not in content:
                    return False
            # Regex or string match
            else:
                if cond not in content and cond not in tool_name:
                    return False
                    
        return True

    def _run_command(self, cmd: str) -> bool:
        try:
            subprocess.run(cmd, shell=True, check=True, cwd=self.project_root)
            return True
        except subprocess.CalledProcessError:
            return False

    def _run_command_capture(self, cmd: str) -> (bool, str):
        try:
            result = subprocess.run(
                cmd, 
                shell=True, 
                capture_output=True, 
                text=True, 
                cwd=self.project_root
            )
            return result.returncode == 0, result.stdout + result.stderr
        except Exception as e:
            return False, str(e)
