"""
Agent Tools - Sandboxed File and Execution Tools
"""

import os
import subprocess
from pathlib import Path
from typing import Optional, Dict, Any

class AgentTools:
    def __init__(self, workspace_root: str):
        self.workspace_root = Path(workspace_root).resolve()

    def _is_safe_path(self, target_path: str) -> bool:
        """Ensure the path is within the workspace root to prevent directory traversal."""
        try:
            target = Path(target_path).resolve()
            return self.workspace_root in target.parents or target == self.workspace_root
        except Exception:
            return False

    def read_file(self, file_path: str) -> str:
        """Auto-approved: Read a file from the workspace."""
        if not self._is_safe_path(file_path):
            return "[SECURITY_VIOLATION] Attempted to read outside workspace."
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                return f.read()
        except Exception as e:
            return f"[ERROR] Could not read file: {e}"

    def list_directory(self, dir_path: str = ".") -> str:
        """Auto-approved: List contents of a directory in the workspace."""
        target = (self.workspace_root / dir_path).resolve()
        if not self._is_safe_path(str(target)):
            return "[SECURITY_VIOLATION] Attempted to list outside workspace."
        try:
            items = os.listdir(target)
            return "\n".join(items)
        except Exception as e:
            return f"[ERROR] Could not list directory: {e}"

    def search_content(self, pattern: str, dir_path: str = ".") -> str:
        """Auto-approved: Grep-like search in the workspace."""
        target = (self.workspace_root / dir_path).resolve()
        if not self._is_safe_path(str(target)):
            return "[SECURITY_VIOLATION] Attempted to search outside workspace."
        try:
            # Basic implementation for now
            results = []
            for root, _, files in os.walk(target):
                for file in files:
                    file_path = Path(root) / file
                    try:
                        with open(file_path, 'r', encoding='utf-8') as f:
                            for i, line in enumerate(f):
                                if pattern in line:
                                    rel_path = file_path.relative_to(self.workspace_root)
                                    results.append(f"{rel_path}:{i+1}: {line.strip()}")
                    except:
                        pass # Ignore binary or unreadable files
            return "\n".join(results) if results else "No matches found."
        except Exception as e:
            return f"[ERROR] Could not search content: {e}"

    def edit_file(self, file_path: str, content: str, require_approval: bool = True) -> str:
        """Requires approval: Write or overwrite a file in the workspace."""
        if not self._is_safe_path(file_path):
            return "[SECURITY_VIOLATION] Attempted to write outside workspace."
        
        if require_approval:
            import time
            import sys
            import os
            # Ensure brynq is in path to import hitl_manager
            src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "src"))
            if src_path not in sys.path:
                sys.path.insert(0, src_path)
                
            from brynq.hitl_manager import request_approval
            import sqlite3
            from brynq.hitl_manager import DB_PATH
            
            print(f"[HITL] Requesting approval for edit to {file_path}...")
            exec_id = request_approval("FS_AGENT", f"WRITE FILE: {file_path}\\n\\n{content}")
            
            # Block and poll until status changes from PENDING
            status = "PENDING"
            while status == "PENDING":
                time.sleep(2)
                conn = sqlite3.connect(DB_PATH)
                c = conn.cursor()
                c.execute("SELECT status FROM approvals WHERE id=?", (exec_id,))
                row = c.fetchone()
                conn.close()
                if row:
                    status = row[0]
                else:
                    status = "DENIED" # Safe fallback
                    
            if status != "APPROVED":
                return "[HITL_DENIED] The human operator denied this file modification."
            print(f"[HITL] Edit to {file_path} approved!")
            
        try:
            os.makedirs(os.path.dirname(file_path), exist_ok=True)
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(content)
            
            # Project Sync Hook
            try:
                from runtime.rooms.room_manager import room_manager
                from runtime.rooms.project_sync import notify_project_file_changed
                notify_project_file_changed(file_path, room_manager)
            except Exception:
                pass

            return f"[SUCCESS] Wrote to {file_path}"
        except Exception as e:
            return f"[ERROR] Could not write file: {e}"

    def run_command(self, command: str, require_approval: bool = True) -> str:
        """Requires approval: Run a sandboxed bash command."""
        if require_approval:
            import time
            import sys
            import os
            src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "src"))
            if src_path not in sys.path:
                sys.path.insert(0, src_path)
                
            from brynq.hitl_manager import request_approval
            import sqlite3
            from brynq.hitl_manager import DB_PATH
            
            print(f"[HITL] Requesting approval for command: {command}...")
            exec_id = request_approval("BASH_AGENT", f"EXECUTE COMMAND:\\n> {command}")
            
            # Block and poll until status changes from PENDING
            status = "PENDING"
            while status == "PENDING":
                time.sleep(2)
                conn = sqlite3.connect(DB_PATH)
                c = conn.cursor()
                c.execute("SELECT status FROM approvals WHERE id=?", (exec_id,))
                row = c.fetchone()
                conn.close()
                if row:
                    status = row[0]
                else:
                    status = "DENIED"
                    
            if status != "APPROVED":
                return "[HITL_DENIED] The human operator denied this command execution."
            print(f"[HITL] Command '{command}' approved!")
            
        try:
            result = subprocess.run(
                command,
                cwd=self.workspace_root,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                timeout=30 # Prevent hanging
            )
            output = result.stdout.strip()
            return f"Exit Code: {result.returncode}\\nOutput:\\n{output}"
        except subprocess.TimeoutExpired:
            return "[ERROR] Command timed out after 30 seconds."
        except Exception as e:
            return f"[ERROR] Could not run command: {e}"

class TestRunnerBridge:
    def __init__(self, workspace_root: str):
        self.workspace_root = Path(workspace_root).resolve()

    def run_pytest(self, test_path: str = "") -> Dict[str, Any]:
        """Run pytest and parse results to feed back to models for auto-fix."""
        try:
            cmd = ["pytest", test_path] if test_path else ["pytest"]
            result = subprocess.run(
                cmd,
                cwd=self.workspace_root,
                capture_output=True,
                text=True
            )
            
            # Simple parse
            passed = result.returncode == 0
            return {
                "status": "success" if passed else "failed",
                "exit_code": result.returncode,
                "output": result.stdout,
                "needs_fix": not passed
            }
        except Exception as e:
            return {
                "status": "error",
                "error": str(e),
                "needs_fix": True
            }
