"""
File System Tool

This tool provides file system operations including reading, writing,
and managing files and directories, particularly useful for codebase access.
"""

import os
import shutil
import glob
from typing import Dict, Any, List, Optional
from pathlib import Path
from tools.tool_registry import BaseTool


class FileSystemTool(BaseTool):
    """Tool for file system operations and codebase access"""
    
    def __init__(self):
        super().__init__(
            name="file_system_tool",
            description="Read, write, and manage files and directories. Supports codebase navigation, file operations, and directory management."
        )
        self.working_directory = os.getcwd()
        self.allowed_extensions = {
            '.py', '.js', '.html', '.css', '.json', '.xml', '.yaml', '.yml',
            '.txt', '.md', '.rst', '.csv', '.sql', '.sh', '.bat', '.dockerfile',
            '.java', '.cpp', '.c', '.h', '.go', '.rs', '.php', '.rb', '.swift',
            '.kt', '.scala', '.clj', '.hs', '.ml', '.r', '.m', '.pl', '.lua'
        }
    
    def execute(self, query: str, context: Dict[str, Any]) -> Any:
        """
        Execute file system operations based on query
        
        Args:
            query: User query containing file operation instructions
            context: Task analysis and other context
            
        Returns:
            File operation result
        """
        query_lower = query.lower()
        
        # Determine operation type
        if 'read' in query_lower or 'show' in query_lower or 'display' in query_lower:
            return self._read_file_operation(query)
        elif 'write' in query_lower or 'create' in query_lower:
            return self._write_file_operation(query)
        elif 'list' in query_lower or 'ls' in query_lower or 'dir' in query_lower:
            return self._list_directory_operation(query)
        elif 'find' in query_lower or 'search' in query_lower:
            return self._find_files_operation(query)
        elif 'delete' in query_lower or 'remove' in query_lower:
            return self._delete_operation(query)
        elif 'copy' in query_lower:
            return self._copy_operation(query)
        elif 'move' in query_lower or 'rename' in query_lower:
            return self._move_operation(query)
        elif 'mkdir' in query_lower or 'create directory' in query_lower:
            return self._create_directory_operation(query)
        else:
            # Default: try to extract file path and read
            return self._read_file_operation(query)
    
    def _extract_file_path(self, query: str) -> Optional[str]:
        """Extract file path from query"""
        import re
        
        # Look for quoted paths
        quoted_paths = re.findall(r'["\']([^"\']+)["\']', query)
        for path in quoted_paths:
            if os.path.exists(path) or '.' in os.path.basename(path):
                return path
        
        # Look for paths with file extensions
        words = query.split()
        for word in words:
            if '.' in word and any(word.endswith(ext) for ext in self.allowed_extensions):
                return word
        
        # Look for directory-like paths
        for word in words:
            if '/' in word or '\\' in word:
                return word
        
        return None
    
    def _read_file_operation(self, query: str) -> Dict[str, Any]:
        """Read file content"""
        file_path = self._extract_file_path(query)
        
        if not file_path:
            return {
                "success": False,
                "error": "No file path found in query",
                "message": "Please specify a file path to read"
            }
        
        try:
            # Make path absolute if relative
            if not os.path.isabs(file_path):
                file_path = os.path.join(self.working_directory, file_path)
            
            if not os.path.exists(file_path):
                return {
                    "success": False,
                    "error": f"File not found: {file_path}",
                    "message": f"The file {file_path} does not exist"
                }
            
            if os.path.isdir(file_path):
                # If it's a directory, list its contents
                return self._list_directory(file_path)
            
            # Read file content
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read()
            
            # Get file info
            file_stat = os.stat(file_path)
            file_info = {
                "path": file_path,
                "size": file_stat.st_size,
                "modified": file_stat.st_mtime,
                "extension": os.path.splitext(file_path)[1]
            }
            
            return {
                "success": True,
                "file_path": file_path,
                "content": content,
                "file_info": file_info,
                "message": f"Successfully read file: {file_path}"
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": str(e),
                "message": f"Error reading file {file_path}: {str(e)}"
            }
    
    def _write_file_operation(self, query: str) -> Dict[str, Any]:
        """Write content to file"""
        import re
        # Extract file path (first quoted string that looks like a path or ends with allowed extension)
        quoted = re.findall(r'"([^"]+)"|\'([^\']+)\'', query)
        quoted = [q[0] or q[1] for q in quoted]
        file_path = None
        content = None
        # Heuristic: if two quoted strings, first is path, second is content
        if len(quoted) >= 2:
            file_path, content = quoted[0], quoted[1]
        else:
            # Try to extract file path and content from the query
            for q in quoted:
                if any(q.endswith(ext) for ext in self.allowed_extensions):
                    file_path = q
                else:
                    content = q
        if not file_path or not content:
            return {
                "success": False,
                "error": "Could not extract file path and content from query",
                "message": "Please specify both a file path and content to write, e.g. write to 'file.txt' 'Hello world'"
            }
        # Make path absolute if relative
        if not os.path.isabs(file_path):
            file_path = os.path.join(self.working_directory, file_path)
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(content)
            return {
                "success": True,
                "file_path": file_path,
                "message": f"Successfully wrote to file: {file_path}"
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e),
                "message": f"Error writing to file {file_path}: {str(e)}"
            }
    
    def _list_directory_operation(self, query: str) -> Dict[str, Any]:
        """List directory contents"""
        # Extract directory path
        dir_path = self._extract_file_path(query)
        
        if not dir_path:
            dir_path = self.working_directory
        
        return self._list_directory(dir_path)
    
    def _list_directory(self, dir_path: str) -> Dict[str, Any]:
        """List contents of a directory"""
        try:
            if not os.path.isabs(dir_path):
                dir_path = os.path.join(self.working_directory, dir_path)
            
            if not os.path.exists(dir_path):
                return {
                    "success": False,
                    "error": f"Directory not found: {dir_path}",
                    "message": f"The directory {dir_path} does not exist"
                }
            
            if not os.path.isdir(dir_path):
                return {
                    "success": False,
                    "error": f"Not a directory: {dir_path}",
                    "message": f"{dir_path} is not a directory"
                }
            
            items = []
            for item in os.listdir(dir_path):
                item_path = os.path.join(dir_path, item)
                item_stat = os.stat(item_path)
                
                items.append({
                    "name": item,
                    "path": item_path,
                    "type": "directory" if os.path.isdir(item_path) else "file",
                    "size": item_stat.st_size if os.path.isfile(item_path) else None,
                    "modified": item_stat.st_mtime,
                    "extension": os.path.splitext(item)[1] if os.path.isfile(item_path) else None
                })
            
            # Sort items: directories first, then files
            items.sort(key=lambda x: (x["type"] != "directory", x["name"].lower()))
            
            return {
                "success": True,
                "directory": dir_path,
                "items": items,
                "total_items": len(items),
                "message": f"Listed {len(items)} items in {dir_path}"
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": str(e),
                "message": f"Error listing directory {dir_path}: {str(e)}"
            }
    
    def _find_files_operation(self, query: str) -> Dict[str, Any]:
        """Find files matching a pattern"""
        import re
        
        # Extract search pattern
        pattern_match = re.search(r'find\s+["\']?([^"\']+)["\']?', query, re.IGNORECASE)
        if not pattern_match:
            pattern_match = re.search(r'search\s+["\']?([^"\']+)["\']?', query, re.IGNORECASE)
        
        if not pattern_match:
            return {
                "success": False,
                "error": "No search pattern found",
                "message": "Please specify a search pattern"
            }
        
        pattern = pattern_match.group(1)
        
        try:
            # Search for files
            found_files = []
            
            # Use glob for pattern matching
            if '*' in pattern or '?' in pattern:
                # Glob pattern
                matches = glob.glob(pattern, recursive=True)
                for match in matches:
                    if os.path.isfile(match):
                        found_files.append(match)
            else:
                # Search by filename or extension
                for root, dirs, files in os.walk(self.working_directory):
                    for file in files:
                        if pattern.lower() in file.lower():
                            found_files.append(os.path.join(root, file))
            
            return {
                "success": True,
                "pattern": pattern,
                "found_files": found_files,
                "count": len(found_files),
                "message": f"Found {len(found_files)} files matching '{pattern}'"
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": str(e),
                "message": f"Error searching for files: {str(e)}"
            }
    
    def _delete_operation(self, query: str) -> Dict[str, Any]:
        """Delete file or directory (with safety checks)"""
        return {
            "success": False,
            "error": "Delete operation disabled for safety",
            "message": "File deletion is disabled for safety reasons"
        }
    
    def _copy_operation(self, query: str) -> Dict[str, Any]:
        """Copy file or directory"""
        return {
            "success": False,
            "error": "Copy operation not implemented",
            "message": "File copying requires more specific implementation"
        }
    
    def _move_operation(self, query: str) -> Dict[str, Any]:
        """Move or rename file"""
        return {
            "success": False,
            "error": "Move operation not implemented", 
            "message": "File moving requires more specific implementation"
        }
    
    def _create_directory_operation(self, query: str) -> Dict[str, Any]:
        """Create directory"""
        return {
            "success": False,
            "error": "Directory creation not implemented",
            "message": "Directory creation requires more specific implementation"
        }
    
    def get_capabilities(self) -> List[str]:
        """Return capabilities of this tool"""
        return [
            "file_reading",
            "directory_listing",
            "file_search",
            "codebase_navigation",
            "file_information",
            "path_resolution"
        ]
    
    def validate_input(self, query: str, context: Dict[str, Any]) -> bool:
        """Validate input for file operations"""
        # Basic validation - ensure query contains file-related keywords
        file_keywords = [
            'file', 'read', 'write', 'list', 'find', 'search', 
            'directory', 'folder', 'path', 'codebase'
        ]
        
        query_lower = query.lower()
        return any(keyword in query_lower for keyword in file_keywords)
    
    def set_working_directory(self, path: str):
        """Set the working directory"""
        if os.path.exists(path) and os.path.isdir(path):
            self.working_directory = os.path.abspath(path)
            return True
        return False
    
    def get_working_directory(self) -> str:
        """Get current working directory"""
        return self.working_directory

