"""Path validator for shell security"""

import os
import re
from typing import List, Optional, Tuple


class PathValidator:
    """Validator for path security"""
    
    def __init__(self, allowed_dirs: List[str]):
        """Initialize path validator
        
        Args:
            allowed_dirs: List of allowed directories
        """
        self._allowed_dirs = [os.path.abspath(d) for d in allowed_dirs]
    
    def is_path_safe(self, path: str) -> bool:
        """Check if a path is safe (within allowed directories)
        
        Args:
            path: Path to validate
            
        Returns:
            bool: True if path is safe
        """
        try:
            abs_path = os.path.abspath(path)
            return self._is_within_allowed(abs_path)
        except (ValueError, OSError):
            return False
    
    def _is_within_allowed(self, abs_path: str) -> bool:
        """Check if absolute path is within allowed directories"""
        if not self._allowed_dirs:
            return True
        
        for allowed_dir in self._allowed_dirs:
            if abs_path.startswith(allowed_dir + os.sep) or abs_path == allowed_dir:
                return True
        
        return False
    
    def validate_command_paths(self, command: str) -> Tuple[bool, Optional[str]]:
        """Validate all paths in a command
        
        Args:
            command: Command string to validate
            
        Returns:
            Tuple[bool, Optional[str]]: (is_safe, error_message)
        """
        paths = self._extract_paths(command)
        
        for path in paths:
            if not self.is_path_safe(path):
                return False, f"Path not allowed: {path}"
        
        return True, None
    
    def _extract_paths(self, command: str) -> List[str]:
        """Extract potential paths from command
        
        Args:
            command: Command string
            
        Returns:
            List of potential paths
        """
        paths = []
        
        path_pattern = r'(?:^|[\s;|&])([A-Za-z]:[\\\/][^\s\)]+|[\\\/][^\s\)]+)'
        matches = re.finditer(path_pattern, command)
        
        for match in matches:
            path = match.group(1).strip()
            if path and not path.startswith('$'):
                paths.append(path)
        
        return paths


class CommandValidator:
    """Command security validator"""
    
    def __init__(
        self,
        command_blacklist: Optional[List[str]] = None,
        allowed_dirs: Optional[List[str]] = None,
        allow_absolute_paths: bool = False,
    ):
        """Initialize command validator
        
        Args:
            command_blacklist: List of blocked command patterns
            allowed_dirs: List of allowed directories
            allow_absolute_paths: Whether to allow absolute paths
        """
        self._command_blacklist = command_blacklist or []
        self._allow_absolute_paths = allow_absolute_paths
        
        if allowed_dirs:
            self._path_validator = PathValidator(allowed_dirs)
        else:
            self._path_validator = None
    
    def validate(self, command: str, cwd: Optional[str] = None) -> Tuple[bool, Optional[str]]:
        """Validate a command for security
        
        Args:
            command: Command to validate
            cwd: Current working directory
            
        Returns:
            Tuple[bool, Optional[str]]: (is_valid, error_message)
        """
        if self._is_command_blocked(command):
            return False, f"Command not allowed (blacklisted): {command}"
        
        if not self._allow_absolute_paths:
            has_absolute = self._has_absolute_path(command)
            if has_absolute:
                return False, "Absolute paths are not allowed"
        
        if self._path_validator:
            is_safe, error = self._path_validator.validate_command_paths(command)
            if not is_safe:
                return False, error
        
        return True, None
    
    def _is_command_blocked(self, command: str) -> bool:
        """Check if command contains blacklisted patterns"""
        if not self._command_blacklist:
            return False
        
        command_lower = command.lower()
        
        for blocked in self._command_blacklist:
            if blocked.lower() in command_lower:
                return True
        
        return False
    
    def _has_absolute_path(self, command: str) -> bool:
        """Check if command contains absolute paths"""
        abs_path_patterns = [
            r'[A-Za-z]:[\\\/]',  # Windows: C:\, D:/
            r'^/[^\s]',          # Unix: /bin
        ]
        
        for pattern in abs_path_patterns:
            if re.search(pattern, command):
                return True
        
        return False


def validate_path_traversal(path: str) -> Tuple[bool, Optional[str]]:
    """Validate path for directory traversal attacks
    
    Args:
        path: Path to validate
        
    Returns:
        Tuple[bool, Optional[str]]: (is_safe, error_message)
    """
    path_parts = path.split(os.sep)
    
    if '..' in path_parts:
        return False, "Path traversal detected (..)"
    
    if path.startswith('~'):
        return False, "Home directory expansion not allowed"
    
    return True, None
