"""
Safety Guards Module

Implements security checks to prevent destructive operations:
- Command blocklist validation
- Path restriction to project directory
- Secret redaction from prompts
- File operation confirmations
- Resource limits
"""

import os
import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional

from rich.console import Console
from rich.prompt import Confirm

console = Console()


@dataclass
class SafetyConfig:
    """Safety configuration settings."""

    confirm_delete: bool = True
    restrict_to_project: bool = True
    max_file_size: int = 1_048_576  # 1MB
    command_timeout: int = 60
    blocked_commands: list[str] = field(default_factory=list)
    project_root: Optional[Path] = None

    def __post_init__(self):
        if not self.blocked_commands:
            # Default dangerous command patterns
            self.blocked_commands = [
                r"rm\s+-rf\s+/",
                r"rm\s+-rf\s+~",
                r"rm\s+-rf\s+\*",
                r"rmdir\s+/s\s+/q\s+[A-Z]:\\",
                r"format\s+[A-Z]:",
                r"del\s+/f\s+/s\s+/q",
                r":\(\)\{:\|:&\};:",  # Fork bomb
                r"mkfs\.",
                r"dd\s+if=.+of=/dev/",
                r"chmod\s+-R\s+777\s+/",
                r"chmod\s+777\s+/",
                r">\s*/dev/sda",
                r"wget.+\|\s*sh",
                r"curl.+\|\s*sh",
                r"curl.+\|\s*bash",
            ]


class SafetyGuard:
    """
    Safety guard implementation for protecting against destructive operations.
    
    This class provides validation for:
    - Shell commands (blocking dangerous patterns)
    - File paths (restricting to project directory)
    - File operations (requiring confirmation for deletes)
    - Content (redacting secrets from prompts)
    """

    # Patterns for detecting secrets in content
    SECRET_PATTERNS = [
        (r"(api[_-]?key\s*[=:]\s*)['\"]?[\w-]{20,}['\"]?", r"\1[REDACTED]"),
        (r"(secret[_-]?key\s*[=:]\s*)['\"]?[\w-]{20,}['\"]?", r"\1[REDACTED]"),
        (r"(password\s*[=:]\s*)['\"]?[^\s'\"]+['\"]?", r"\1[REDACTED]"),
        (r"(token\s*[=:]\s*)['\"]?[\w-]{20,}['\"]?", r"\1[REDACTED]"),
        (r"(bearer\s+)[\w-]{20,}", r"\1[REDACTED]"),
        (r"(sk-[a-zA-Z0-9]{20,})", r"[REDACTED_API_KEY]"),
        (r"(sk-ant-[a-zA-Z0-9-]{20,})", r"[REDACTED_API_KEY]"),
        (r"(ghp_[a-zA-Z0-9]{20,})", r"[REDACTED_GITHUB_TOKEN]"),
        (r"(gho_[a-zA-Z0-9]{20,})", r"[REDACTED_GITHUB_TOKEN]"),
    ]

    def __init__(self, config: Optional[SafetyConfig] = None):
        self.config = config or SafetyConfig()

    def set_project_root(self, path: Path) -> None:
        """Set the project root directory for path restrictions."""
        self.config.project_root = path.resolve()

    def validate_command(self, command: str) -> tuple[bool, Optional[str]]:
        """
        Validate a shell command against the blocklist.
        
        Args:
            command: The shell command to validate
            
        Returns:
            Tuple of (is_safe, error_message)
        """
        command_lower = command.lower()

        for pattern in self.config.blocked_commands:
            if re.search(pattern, command_lower, re.IGNORECASE):
                return False, f"Command blocked by safety guard: matches pattern '{pattern}'"

        return True, None

    def validate_path(self, path: str | Path, operation: str = "access") -> tuple[bool, Optional[str]]:
        """
        Validate a file path is within the allowed project directory.
        
        Args:
            path: The file path to validate
            operation: The operation being performed (for error messages)
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        if not self.config.restrict_to_project:
            return True, None

        if self.config.project_root is None:
            return True, None

        try:
            resolved_path = Path(path).resolve()
            project_root = self.config.project_root.resolve()

            # Check if path is within project root
            try:
                resolved_path.relative_to(project_root)
                return True, None
            except ValueError:
                return False, (
                    f"Path '{path}' is outside project directory. "
                    f"Operation '{operation}' is restricted to: {project_root}"
                )
        except Exception as e:
            return False, f"Invalid path '{path}': {e}"

    def validate_file_size(self, path: str | Path) -> tuple[bool, Optional[str]]:
        """
        Check if a file is within the allowed size limit.
        
        Args:
            path: Path to the file to check
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        try:
            file_path = Path(path)
            if not file_path.exists():
                return True, None  # Non-existent files are fine (for creation)

            size = file_path.stat().st_size
            if size > self.config.max_file_size:
                return False, (
                    f"File '{path}' is {size:,} bytes, exceeding limit of "
                    f"{self.config.max_file_size:,} bytes"
                )
            return True, None
        except Exception as e:
            return False, f"Cannot check file size for '{path}': {e}"

    def require_delete_confirmation(self, path: str | Path) -> bool:
        """
        Prompt for confirmation before file deletion.
        
        Args:
            path: Path to the file being deleted
            
        Returns:
            True if confirmed, False otherwise
        """
        if not self.config.confirm_delete:
            return True

        console.print(f"\n[bold yellow]⚠ DELETE CONFIRMATION[/bold yellow]")
        console.print(f"  File: [cyan]{path}[/cyan]")

        return Confirm.ask("  Are you sure you want to delete this file?", default=False)

    def require_write_confirmation(self, path: str | Path, diff: Optional[str] = None) -> bool:
        """
        Prompt for confirmation before file write.
        
        Args:
            path: Path to the file being written
            diff: Optional diff to display
            
        Returns:
            True if confirmed, False otherwise
        """
        console.print(f"\n[bold blue]📝 WRITE CONFIRMATION[/bold blue]")
        console.print(f"  File: [cyan]{path}[/cyan]")

        if diff:
            console.print("\n[dim]Changes:[/dim]")
            console.print(diff)

        return Confirm.ask("\n  Apply these changes?", default=True)

    def redact_secrets(self, content: str) -> str:
        """
        Redact sensitive information from content before sending to LLM.
        
        Args:
            content: The content to redact
            
        Returns:
            Content with secrets redacted
        """
        redacted = content

        for pattern, replacement in self.SECRET_PATTERNS:
            redacted = re.sub(pattern, replacement, redacted, flags=re.IGNORECASE)

        return redacted

    def validate_all(
        self, 
        command: Optional[str] = None, 
        path: Optional[str | Path] = None,
        operation: str = "access"
    ) -> tuple[bool, list[str]]:
        """
        Run all applicable validations.
        
        Args:
            command: Optional command to validate
            path: Optional path to validate
            operation: The operation being performed
            
        Returns:
            Tuple of (all_valid, list_of_errors)
        """
        errors = []

        if command:
            is_safe, error = self.validate_command(command)
            if not is_safe:
                errors.append(error)

        if path:
            is_valid, error = self.validate_path(path, operation)
            if not is_valid:
                errors.append(error)

            is_valid, error = self.validate_file_size(path)
            if not is_valid:
                errors.append(error)

        return len(errors) == 0, errors


# Singleton instance for global access
_guard_instance: Optional[SafetyGuard] = None


def get_safety_guard() -> SafetyGuard:
    """Get the global safety guard instance."""
    global _guard_instance
    if _guard_instance is None:
        _guard_instance = SafetyGuard()
    return _guard_instance


def configure_safety(config: SafetyConfig) -> SafetyGuard:
    """Configure and return the global safety guard."""
    global _guard_instance
    _guard_instance = SafetyGuard(config)
    return _guard_instance
