#!/usr/bin/env python3
"""
Visual diff display utility for SWE Agent interfaces.
Shows code changes with line numbers, additions, and removals.
"""

import difflib
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax
from rich.text import Text
import time
import hashlib

console = Console()

class DiffDisplay:
    """
    Visual diff display for code changes.
    Tracks file modifications and shows colored diffs.
    """
    
    def __init__(self):
        self.file_snapshots: Dict[str, str] = {}
        self.file_timestamps: Dict[str, float] = {}
        
    def take_snapshot(self, file_path: str) -> None:
        """Take a snapshot of a file's current content."""
        try:
            path = Path(file_path)
            if path.exists() and path.is_file():
                with open(path, 'r', encoding='utf-8', errors='ignore') as f:
                    content = f.read()
                self.file_snapshots[file_path] = content
                self.file_timestamps[file_path] = time.time()
        except Exception:
            # Silently handle file read errors
            pass
    
    def get_diff(self, file_path: str) -> Optional[Tuple[str, str, List[str]]]:
        """
        Get diff for a file if it has changed since snapshot.
        Returns: (old_content, new_content, diff_lines) or None if no changes
        """
        try:
            path = Path(file_path)
            if not path.exists() or not path.is_file():
                return None
                
            # Read current content
            with open(path, 'r', encoding='utf-8', errors='ignore') as f:
                current_content = f.read()
            
            # Check if we have a snapshot
            if file_path not in self.file_snapshots:
                # No snapshot, take one now
                self.take_snapshot(file_path)
                return None
            
            old_content = self.file_snapshots[file_path]
            
            # Check if content actually changed
            if old_content == current_content:
                return None
            
            # Generate unified diff
            old_lines = old_content.splitlines(keepends=True)
            new_lines = current_content.splitlines(keepends=True)
            
            diff_lines = list(difflib.unified_diff(
                old_lines,
                new_lines,
                fromfile=f"a/{file_path}",
                tofile=f"b/{file_path}",
                lineterm=''
            ))
            
            return old_content, current_content, diff_lines
            
        except Exception:
            return None
    
    def display_diff(self, file_path: str, max_lines: int = 50) -> bool:
        """
        Display visual diff for a file.
        Returns True if diff was shown, False if no changes.
        """
        diff_result = self.get_diff(file_path)
        if not diff_result:
            return False
        
        old_content, new_content, diff_lines = diff_result
        
        if not diff_lines:
            return False
        
        # Update snapshot after getting diff
        self.file_snapshots[file_path] = new_content
        self.file_timestamps[file_path] = time.time()
        
        # Parse diff and create display
        diff_text = Text()
        lines_shown = 0
        
        for line in diff_lines:
            if lines_shown >= max_lines:
                diff_text.append(f"\n... (truncated, {len(diff_lines) - lines_shown} more lines)\n", style="dim")
                break
                
            if line.startswith('@@'):
                # Hunk header
                diff_text.append(line, style="cyan")
            elif line.startswith('+') and not line.startswith('+++'):
                # Addition
                diff_text.append(line, style="green")
            elif line.startswith('-') and not line.startswith('---'):
                # Deletion
                diff_text.append(line, style="red")
            elif line.startswith('+++') or line.startswith('---'):
                # File headers
                diff_text.append(line, style="bold white")
            else:
                # Context line
                diff_text.append(line, style="white")
            
            diff_text.append("\n")
            lines_shown += 1
        
        # Display in a panel
        panel = Panel(
            diff_text,
            title=f"[bold]Update: {file_path}[/bold]",
            border_style="blue",
            expand=False
        )
        
        console.print(panel)
        return True
    
    def display_multiple_diffs(self, file_paths: List[str], max_lines_per_file: int = 30) -> int:
        """
        Display diffs for multiple files.
        Returns number of files with changes displayed.
        """
        changes_count = 0
        
        for file_path in file_paths:
            if self.display_diff(file_path, max_lines_per_file):
                changes_count += 1
        
        return changes_count
    
    def take_project_snapshot(self, project_path: str = ".", patterns: List[str] = None) -> None:
        """
        Take snapshots of all relevant files in a project.
        """
        if patterns is None:
            patterns = ["*.py", "*.js", "*.ts", "*.jsx", "*.tsx", "*.java", "*.cpp", "*.c", "*.h", 
                       "*.go", "*.rs", "*.php", "*.rb", "*.swift", "*.kt", "*.cs", "*.html", 
                       "*.css", "*.scss", "*.json", "*.yaml", "*.yml", "*.md", "*.txt"]
        
        path = Path(project_path)
        for pattern in patterns:
            for file_path in path.rglob(pattern):
                if file_path.is_file() and not any(part.startswith('.') for part in file_path.parts):
                    self.take_snapshot(str(file_path))
    
    def get_changed_files(self) -> List[str]:
        """Get list of files that have changed since last snapshot."""
        changed_files = []
        
        for file_path in self.file_snapshots.keys():
            if self.get_diff(file_path):
                changed_files.append(file_path)
        
        return changed_files

# Global diff display instance
diff_display = DiffDisplay()

def show_code_diff(file_path: str) -> bool:
    """Convenience function to show diff for a single file."""
    return diff_display.display_diff(file_path)

def show_project_diffs(file_paths: List[str] = None) -> int:
    """Convenience function to show diffs for multiple files."""
    if file_paths is None:
        file_paths = diff_display.get_changed_files()
    return diff_display.display_multiple_diffs(file_paths)

def take_project_snapshot(project_path: str = ".") -> None:
    """Convenience function to take project snapshot."""
    diff_display.take_project_snapshot(project_path)