"""
Video analyzer core module for bird detection in videos using YOLOv8
"""

import cv2
import subprocess
import tempfile
import numpy as np
from pathlib import Path
from datetime import timedelta
from ultralytics import YOLO
from .i18n import t

# Try to import PIL for Unicode text rendering
try:
    from PIL import Image, ImageDraw, ImageFont
    PIL_AVAILABLE = True
except ImportError:
    PIL_AVAILABLE = False

# Optional species classification
try:
    from .species_classifier import BirdSpeciesClassifier, aggregate_species_detections
    SPECIES_AVAILABLE = True
except ImportError:
    SPECIES_AVAILABLE = False
    BirdSpeciesClassifier = None
    aggregate_species_detections = None


def put_unicode_text(img, text, position, font_size=30, color=(255, 255, 255), bg_color=None):
    """
    Draw Unicode text (including emojis) on image using PIL
    
    Args:
        img: OpenCV image (numpy array, BGR)
        text: Text to draw (can contain Unicode/emojis)
        position: (x, y) position tuple
        font_size: Font size in pixels
        color: Text color in BGR format
        bg_color: Background color in BGR format (None = transparent)
        
    Returns:
        Modified image with text
    """
    if not PIL_AVAILABLE:
        # Fallback to cv2.putText if PIL not available
        print("WARNING: PIL not available, using cv2.putText fallback")
        cv2.putText(img, text, position, cv2.FONT_HERSHEY_SIMPLEX, 
                   font_size/30, color, 2, cv2.LINE_AA)
        return img
    
    # Make a copy to avoid modifying original
    img = img.copy()
    
    # Convert BGR to RGB for PIL
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img_pil)
    
    # Try to load a font that supports Unicode (including CJK characters)
    try:
        # For best results, use a font that supports BOTH Latin and CJK characters
        # DejaVu has good Latin support, Droid has CJK support
        # Try fonts that support both
        font_paths = [
            # Fonts with BOTH Latin and CJK support
            '/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf',  # Noto Sans (good Latin)
            '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',  # DejaVu (excellent Latin)
            '/usr/share/fonts/TTF/DejaVuSans.ttf',              # Arch
            '/System/Library/Fonts/Helvetica.ttc',              # macOS
            'C:\\Windows\\Fonts\\arial.ttf',                    # Windows
        ]
        
        font = None
        for font_path in font_paths:
            if Path(font_path).exists():
                try:
                    font = ImageFont.truetype(font_path, font_size)
                    break
                except:
                    continue
        
        if font is None:
            font = ImageFont.load_default()
    except:
        font = ImageFont.load_default()
    
    # For Japanese/CJK fallback, we need to use a composite approach
    # PIL doesn't support font fallback well, so we'll just use DejaVu which is most reliable
    # Note: Japanese characters may not render perfectly, but Latin text will be clear
    
    # Get text bounding box for background
    bbox = draw.textbbox(position, text, font=font)
    
    # Draw background if specified
    if bg_color is not None:
        # Convert BGR to RGB
        bg_rgb = (bg_color[2], bg_color[1], bg_color[0])
        padding = 5
        draw.rectangle(
            [bbox[0] - padding, bbox[1] - padding, 
             bbox[2] + padding, bbox[3] + padding],
            fill=bg_rgb
        )
    
    # Draw text (convert BGR to RGB)
    text_rgb = (color[2], color[1], color[0])
    draw.text(position, text, font=font, fill=text_rgb)
    
    # Convert back to BGR for OpenCV
    img_cv = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
    
    return img_cv


class VideoAnalyzer:
    """Analyzes videos for bird content using YOLOv8"""
    
    def __init__(self, model_path="yolov8n.pt", threshold=0.3, target_class=14, identify_species=False, species_model="dima806/bird_species_image_detection", species_threshold=0.3):
        """
        Initialize the analyzer
        
        Args:
            model_path: Path to YOLO model (searches: models/, config/models/, current dir, auto-download)
            threshold: Confidence threshold (0.0-1.0), default 0.3 for bird detection
            target_class: COCO class for bird (14=bird)
            identify_species: Enable bird species classification (requires species dependencies)
            species_model: Hugging Face model for species classification (default: dima806/bird_species_image_detection)
            species_threshold: Minimum confidence threshold for species classification (default: 0.3)
        """
        model_path = self._find_model(model_path)
        print(f"🤖 {t('loading_model')} {model_path}")
        self.model = YOLO(model_path)
        self.threshold = threshold
        self.target_class = target_class
        self.identify_species = identify_species
        self.species_classifier = None
        
        # Initialize species classifier if requested
        if self.identify_species:
            if not SPECIES_AVAILABLE:
                print(f"   ⚠️  Species identification requires additional dependencies.")
                print(f"   Install with: pip install vogel-video-analyzer[species]")
                print(f"   Continuing with basic bird detection only.\n")
                self.identify_species = False
            else:
                try:
                    self.species_classifier = BirdSpeciesClassifier(model_name=species_model, confidence_threshold=species_threshold)
                except Exception as e:
                    print(f"   ⚠️  Could not load species classifier: {e}")
                    print(f"   Continuing with basic bird detection only.\n")
                    self.identify_species = False
    
    def _find_model(self, model_name):
        """
        Search for model in various directories
        
        Search paths (in order):
        1. models/
        2. config/models/
        3. Current directory
        4. Let Ultralytics auto-download
        
        Args:
            model_name: Name or path of model
            
        Returns:
            Path to model or original name for auto-download
        """
        # If absolute path provided
        if Path(model_name).is_absolute() and Path(model_name).exists():
            return model_name
        
        # Define search paths
        search_paths = [
            Path('models') / model_name,
            Path('config/models') / model_name,
            Path(model_name)
        ]
        
        # Search in directories
        for path in search_paths:
            if path.exists():
                return str(path)
        
        # Not found → Ultralytics downloads automatically
        print(f"   ℹ️  {t('model_not_found').format(model_name=model_name)}")
        return model_name
        
    def analyze_video(self, video_path, sample_rate=5):
        """
        Analyze video frame by frame
        
        Args:
            video_path: Path to MP4 video
            sample_rate: Analyze every Nth frame (1=all, 5=every 5th, etc.)
            
        Returns:
            dict with statistics
        """
        video_path = Path(video_path)
        if not video_path.exists():
            raise FileNotFoundError(t('video_not_found').format(path=str(video_path)))
            
        print(f"\n📹 {t('analyzing')} {video_path.name}")
        
        cap = cv2.VideoCapture(str(video_path))
        if not cap.isOpened():
            raise RuntimeError(t('cannot_open_video').format(path=str(video_path)))
            
        # Video properties
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        fps = cap.get(cv2.CAP_PROP_FPS)
        duration = total_frames / fps if fps > 0 else 0
        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        
        print(f"   📊 {t('video_info')} {width}x{height}, {fps:.1f} FPS, {duration:.1f}s, {total_frames} {t('frames')}")
        
        # Analysis variables
        frames_analyzed = 0
        frames_with_birds = 0
        bird_detections = []
        current_frame = 0
        
        print(f"   🔍 {t('analyzing_every_nth').format(n=sample_rate)}")
        
        while True:
            ret, frame = cap.read()
            if not ret:
                break
                
            current_frame += 1
            
            # Apply sample rate
            if current_frame % sample_rate != 0:
                continue
                
            frames_analyzed += 1
            
            # YOLO inference
            results = self.model(frame, verbose=False)
            
            # Check bird detection
            birds_in_frame = 0
            frame_species = []
            
            for result in results:
                boxes = result.boxes
                for box in boxes:
                    cls = int(box.cls[0])
                    conf = float(box.conf[0])
                    
                    if cls == self.target_class and conf >= self.threshold:
                        birds_in_frame += 1
                        
                        # Species identification if enabled
                        if self.identify_species and self.species_classifier:
                            try:
                                # Get bounding box coordinates
                                xyxy = box.xyxy[0].cpu().numpy()
                                x1, y1, x2, y2 = int(xyxy[0]), int(xyxy[1]), int(xyxy[2]), int(xyxy[3])
                                bbox = (x1, y1, x2, y2)
                                
                                # Classify the bird crop
                                species_predictions = self.species_classifier.classify_crop(
                                    frame, bbox, top_k=1
                                )
                                
                                if species_predictions:
                                    species_info = species_predictions[0]
                                    # Translate species name to current language
                                    translated_name = BirdSpeciesClassifier.format_species_name(
                                        species_info['label'], translate=True
                                    )
                                    frame_species.append({
                                        'species': translated_name,
                                        'confidence': species_info['score']
                                    })
                            except Exception as e:
                                # Log error for debugging
                                import sys
                                print(f"   ⚠️  Species classification error (frame {current_frame}): {e}", file=sys.stderr)
                                import traceback
                                traceback.print_exc()
                                pass
                        
            if birds_in_frame > 0:
                frames_with_birds += 1
                timestamp = current_frame / fps if fps > 0 else 0
                detection_entry = {
                    'frame': current_frame,
                    'timestamp': timestamp,
                    'birds': birds_in_frame
                }
                
                # Add species information if available
                if frame_species:
                    detection_entry['species'] = frame_species
                
                bird_detections.append(detection_entry)
                
            # Progress every 30 analyzed frames
            if frames_analyzed % 30 == 0:
                progress = (frames_analyzed * sample_rate / total_frames) * 100
                print(f"   ⏳ {progress:.1f}% ({frames_analyzed}/{total_frames//sample_rate} {t('frames')})", end='\r')
                
        cap.release()
        
        # Calculate statistics
        bird_percentage = (frames_with_birds / frames_analyzed * 100) if frames_analyzed > 0 else 0
        
        # Find continuous bird segments
        segments = self._find_bird_segments(bird_detections, fps, sample_rate)
        
        stats = {
            'video_file': video_path.name,
            'video_path': str(video_path),
            'resolution': f"{width}x{height}",
            'fps': fps,
            'duration_seconds': duration,
            'total_frames': total_frames,
            'frames_analyzed': frames_analyzed,
            'sample_rate': sample_rate,
            'frames_with_birds': frames_with_birds,
            'bird_percentage': bird_percentage,
            'bird_detections': len(bird_detections),
            'bird_segments': segments,
            'threshold': self.threshold,
            'model': str(self.model.ckpt_path if hasattr(self.model, 'ckpt_path') else 'unknown')
        }
        
        # Add species statistics if species identification was enabled
        if self.identify_species and SPECIES_AVAILABLE:
            # Extract all species detections from bird_detections
            all_species = []
            for detection in bird_detections:
                if 'species' in detection:
                    all_species.extend(detection['species'])
            
            if all_species:
                species_stats = aggregate_species_detections(all_species)
                stats['species_stats'] = species_stats
        
        print(f"\n   ✅ {t('analysis_complete')}")
        return stats
        
    def _find_bird_segments(self, detections, fps, sample_rate):
        """
        Find continuous time segments with bird presence
        
        Args:
            detections: List of bird detections
            fps: Video FPS
            sample_rate: Frame sample rate
            
        Returns:
            List of segments with start/end times
        """
        if not detections:
            return []
            
        segments = []
        current_segment = None
        max_gap = 2.0 * sample_rate  # Max 2 second gap
        
        for detection in detections:
            timestamp = detection['timestamp']
            
            if current_segment is None:
                # Start new segment
                current_segment = {
                    'start': timestamp,
                    'end': timestamp,
                    'detections': 1
                }
            elif timestamp - current_segment['end'] <= max_gap:
                # Extend segment
                current_segment['end'] = timestamp
                current_segment['detections'] += 1
            else:
                # End segment and start new one
                segments.append(current_segment)
                current_segment = {
                    'start': timestamp,
                    'end': timestamp,
                    'detections': 1
                }
                
        # Add last segment
        if current_segment:
            segments.append(current_segment)
            
        return segments
        
    def print_report(self, stats):
        """
        Print formatted report
        
        Args:
            stats: Statistics dictionary
        """
        print(f"\n🎬 {t('report_title')}")
        print("━" * 70)
        
        print(f"\n📁 {t('report_file')} {stats['video_path']}")
        print(f"📊 {t('report_total_frames')} {stats['total_frames']} ({t('report_analyzed')} {stats['frames_analyzed']})")
        print(f"⏱️  {t('report_duration')} {stats['duration_seconds']:.1f} {t('report_seconds')}")
        print(f"🐦 {t('report_bird_frames')} {stats['frames_with_birds']} ({stats['bird_percentage']:.1f}%)")
        print(f"🎯 {t('report_bird_segments')} {len(stats['bird_segments'])}")
        
        if stats['bird_segments']:
            print(f"\n📍 {t('report_detected_segments')}")
            for i, segment in enumerate(stats['bird_segments'], 1):
                start = timedelta(seconds=int(segment['start']))
                end = timedelta(seconds=int(segment['end']))
                duration = segment['end'] - segment['start']
                bird_pct = (segment['detections'] / stats['frames_analyzed']) * 100
                print(f"  {'┌' if i == 1 else '├'} {t('report_segment')} {i}: {start} - {end} ({bird_pct:.0f}% {t('report_bird_frames_short')})")
                if i == len(stats['bird_segments']):
                    print(f"  └")
        
        # Status
        if stats['bird_percentage'] >= 50:
            print(f"\n✅ {t('report_status')} {t('status_significant')}")
        elif stats['bird_percentage'] > 0:
            print(f"\n⚠️  {t('report_status')} {t('status_limited')}")
        else:
            print(f"\n❌ {t('report_status')} {t('status_none')}")
        
        # Species identification results
        if 'species_stats' in stats and stats['species_stats']:
            species_stats = stats['species_stats']
            print(f"\n🦜 {t('species_title')}")
            if species_stats:
                print(f"   {t('species_count').format(count=len(species_stats))}")
                print()
                for species_name, data in sorted(species_stats.items(), 
                                                  key=lambda x: x[1]['count'], 
                                                  reverse=True):
                    count = data['count']
                    avg_conf = data['avg_confidence']
                    print(f"  • {species_name}")
                    print(f"    {t('species_detections').format(detections=count)} ({t('species_avg_confidence')}: {avg_conf:.2f})")
            else:
                print(f"   {t('species_no_detections')}")
        
        print("━" * 70)

    def annotate_video(self, video_path, output_path, sample_rate=1, show_timestamp=True, show_confidence=True, box_color=(0, 255, 0), text_color=(255, 255, 255), multilingual=False):
        """
        Create annotated video with bounding boxes and species labels
        
        Args:
            video_path: Path to input video
            output_path: Path for output annotated video
            sample_rate: Process every Nth frame (1=all frames)
            show_timestamp: Display timestamp on video
            show_confidence: Display confidence scores
            box_color: BGR color for bounding boxes (default: green)
            text_color: BGR color for text labels (default: white)
            multilingual: Show bird names in all languages with flags (default: False)
            
        Returns:
            dict with processing statistics
        """
        video_path = Path(video_path)
        output_path = Path(output_path)
        
        if not video_path.exists():
            raise FileNotFoundError(t('video_not_found').format(path=str(video_path)))
            
        print(f"\n🎬 {t('annotation_creating')} {video_path.name}")
        print(f"{t('annotation_output')} {output_path}")
        
        # Open input video
        cap = cv2.VideoCapture(str(video_path))
        if not cap.isOpened():
            raise RuntimeError(t('cannot_open_video').format(path=str(video_path)))
            
        # Video properties
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        fps = cap.get(cv2.CAP_PROP_FPS)
        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        
        # Handle high framerates that exceed codec limits
        # MPEG4 timebase denominator max is 65535, which limits FPS to ~65
        output_fps = fps
        if fps > 60:
            output_fps = 30.0  # Reduce to standard 30 FPS for compatibility
            print(f"   ℹ️  Original FPS ({fps:.1f}) exceeds codec limits, reducing output to {output_fps} FPS")
        
        # Create output video writer
        # Try different codecs for better compatibility
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter(str(output_path), fourcc, output_fps, (width, height))
        
        # Check if writer opened successfully
        if not out.isOpened():
            # Fallback to XVID with AVI container
            print(f"   ⚠️  MP4V codec not available, trying XVID with AVI...")
            fourcc = cv2.VideoWriter_fourcc(*'XVID')
            output_path_avi = output_path.parent / (output_path.stem + '.avi')
            out = cv2.VideoWriter(str(output_path_avi), fourcc, output_fps, (width, height))
            if not out.isOpened():
                raise RuntimeError(f"Could not open video writer. Try installing ffmpeg: sudo apt install ffmpeg")
            output_path = output_path_avi
            print(f"   ℹ️  Output changed to: {output_path}")
        
        print(f"   📊 {t('annotation_video_info').format(width=width, height=height, fps=f'{fps:.1f}', output_fps=f'{output_fps:.1f}', frames=total_frames)}")
        print(f"   🔍 {t('annotation_processing').format(n=sample_rate)}")


        
        # Processing variables
        current_frame = 0
        frames_processed = 0
        total_birds_detected = 0
        
        # Cache for last detections (to avoid flickering)
        last_detections = []
        last_birds_count = 0
        
        while True:
            ret, frame = cap.read()
            if not ret:
                break
                
            current_frame += 1
            annotated_frame = frame.copy()
            
            # Process frame if matches sample rate
            if current_frame % sample_rate == 0:
                frames_processed += 1
                
                # YOLO inference
                results = self.model(frame, verbose=False)
                
                # Clear and rebuild detection cache
                last_detections = []
                birds_in_frame = 0
                
                for result in results:
                    boxes = result.boxes
                    for box in boxes:
                        cls = int(box.cls[0])
                        conf = float(box.conf[0])
                        
                        if cls == self.target_class and conf >= self.threshold:
                            birds_in_frame += 1
                            total_birds_detected += 1
                            
                            # Get bounding box coordinates
                            xyxy = box.xyxy[0].cpu().numpy()
                            x1, y1, x2, y2 = int(xyxy[0]), int(xyxy[1]), int(xyxy[2]), int(xyxy[3])
                            
                            # Species identification if enabled
                            species_label = None
                            if self.identify_species and self.species_classifier:
                                try:
                                    bbox = (x1, y1, x2, y2)
                                    species_predictions = self.species_classifier.classify_crop(
                                        frame, bbox, top_k=1
                                    )
                                    
                                    if species_predictions:
                                        species_info = species_predictions[0]
                                        
                                        # Use multilingual name if requested
                                        if multilingual:
                                            # Use full Unicode format with emojis if PIL available
                                            bird_name = BirdSpeciesClassifier.get_multilingual_name(
                                                species_info['label'].upper(), 
                                                show_flags=PIL_AVAILABLE,
                                                opencv_compatible=not PIL_AVAILABLE
                                            )
                                        else:
                                            bird_name = BirdSpeciesClassifier.format_species_name(
                                                species_info['label'], translate=True
                                            )
                                        
                                        if show_confidence:
                                            species_label = f"{bird_name} {species_info['score']:.0%}"
                                        else:
                                            species_label = bird_name
                                except Exception as e:
                                    species_label = "Bird"
                            else:
                                # No species classification
                                if show_confidence:
                                    species_label = f"Bird {conf:.0%}"
                                else:
                                    species_label = "Bird"
                            
                            # Store detection for reuse
                            last_detections.append({
                                'bbox': (x1, y1, x2, y2),
                                'label': species_label
                            })
                
                last_birds_count = birds_in_frame
            
            # Draw all cached detections (even on non-processed frames)
            for detection in last_detections:
                x1, y1, x2, y2 = detection['bbox']
                label = detection['label']
                
                # Draw bounding box
                cv2.rectangle(annotated_frame, (x1, y1), (x2, y2), box_color, 2)
                
                # Use PIL for Unicode text if available, otherwise fallback to cv2
                if PIL_AVAILABLE and multilingual:
                    # Extract bird name and confidence
                    if '%' in label:
                        bird_part, conf_part = label.rsplit(' ', 1)
                    else:
                        bird_part = label
                        conf_part = ""
                    
                    # Get individual translations
                    try:
                        species_display = bird_part  # Already just the German name, no emojis
                        from .species_classifier import GERMAN_TO_ENGLISH, BIRD_NAME_TRANSLATIONS
                        species_key = GERMAN_TO_ENGLISH.get(species_display.lower())
                        
                        if species_key:
                            # Get translations
                            en_name = ' '.join(word.capitalize() for word in species_key.split())
                            de_name = BIRD_NAME_TRANSLATIONS.get('de', {}).get(species_key, en_name)
                            
                            # Multiline format without emojis
                            # Line 1: EN: English name
                            # Line 2: DE: German name  
                            # Line 3: Confidence
                            lines = [
                                f"EN: {en_name}",
                                f"DE: {de_name}",
                                conf_part
                            ]
                        else:
                            lines = [label]
                    except:
                        lines = [label]
                    
                    # Draw multiline with larger font
                    line_height = 45  # Increased from 40
                    total_height = len(lines) * line_height + 20  # More padding
                    
                    # Calculate position - place box ABOVE the bounding box to avoid covering bird
                    box_y_start = max(0, y1 - total_height - 10)  # 10px gap above bird box
                    box_y_end = y1 - 10
                    
                    # White background for better contrast - wider box
                    cv2.rectangle(
                        annotated_frame,
                        (x1, box_y_start),
                        (x1 + 550, box_y_end),  # Wider: 550 instead of 500
                        (255, 255, 255),  # White background
                        -1
                    )
                    
                    # Draw each line with black text
                    for i, line in enumerate(lines):
                        annotated_frame = put_unicode_text(
                            annotated_frame,
                            line,
                            (x1 + 12, box_y_start + 12 + i * line_height),  # More padding: 12 instead of 10
                            font_size=34 if i < len(lines) - 1 else 38,  # Even larger: 34pt for names, 38pt for confidence
                            color=(0, 0, 0),  # Black text
                            bg_color=None  # Background already drawn
                        )
                else:
                    # Fallback to OpenCV text (ASCII only)
                    (text_width, text_height), baseline = cv2.getTextSize(
                        label, cv2.FONT_HERSHEY_SIMPLEX, 0.9, 3
                    )
                    
                    # Background rectangle
                    cv2.rectangle(
                        annotated_frame,
                        (x1, y1 - text_height - 15),
                        (x1 + text_width + 15, y1),
                        box_color,
                        -1
                    )
                    
                    # Text
                    cv2.putText(
                        annotated_frame,
                        label,
                        (x1 + 7, y1 - 7),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.9,
                        text_color,
                        3
                    )
            
            # Add frame info overlay
            if show_timestamp:
                timestamp = current_frame / fps if fps > 0 else 0
                timestamp_str = str(timedelta(seconds=int(timestamp)))
                info_text = f"Frame: {current_frame}/{total_frames} | Time: {timestamp_str}"
                
                if last_birds_count > 0:
                    info_text += f" | Birds: {last_birds_count}"
                    
                    # Background for timestamp
                    (info_width, info_height), _ = cv2.getTextSize(
                        info_text, cv2.FONT_HERSHEY_SIMPLEX, 0.9, 3
                    )
                    cv2.rectangle(
                        annotated_frame,
                        (10, height - info_height - 25),
                        (info_width + 25, height - 10),
                        (0, 0, 0),
                        -1
                    )
                    
                    # Timestamp text
                    cv2.putText(
                        annotated_frame,
                        info_text,
                        (17, height - 17),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.9,
                        (255, 255, 255),
                        3
                    )
            
            # Write frame to output
            out.write(annotated_frame)
            
            # Progress indicator
            if current_frame % 100 == 0:
                progress = (current_frame / total_frames) * 100
                print(f"   Progress: {progress:.1f}% ({current_frame}/{total_frames})", end='\r')
        
        # Cleanup
        cap.release()
        out.release()
        
        print(f"\n{t('annotation_complete')}")
        print(f"{t('annotation_frames_processed').format(processed=frames_processed, total=total_frames)}")
        print(f"{t('annotation_birds_detected').format(count=total_birds_detected)}")
        
        # Try to merge audio from original video using ffmpeg
        try:
            # Check if ffmpeg is available
            subprocess.run(['ffmpeg', '-version'], 
                          capture_output=True, check=True, timeout=5)
            
            # Check if original video has audio
            probe_cmd = [
                'ffprobe', '-v', 'error',
                '-select_streams', 'a:0',
                '-show_entries', 'stream=codec_type',
                '-of', 'default=noprint_wrappers=1:nokey=1',
                str(video_path)
            ]
            
            probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=10)
            has_audio = probe_result.stdout.strip() == 'audio'
            
            if has_audio:
                print(f"{t('annotation_merging_audio')}")
                
                # Create temporary file for video without audio
                temp_video = output_path.parent / f"{output_path.stem}_temp{output_path.suffix}"
                output_path.rename(temp_video)
                
                # Merge audio using ffmpeg
                ffmpeg_cmd = [
                    'ffmpeg', '-y',
                    '-i', str(temp_video),      # Video input (annotated, no audio)
                    '-i', str(video_path),       # Original video (with audio)
                    '-c:v', 'copy',              # Copy video stream
                    '-c:a', 'aac',               # Re-encode audio to AAC
                    '-map', '0:v:0',             # Take video from first input
                    '-map', '1:a:0',             # Take audio from second input
                    '-shortest',                 # Match shortest stream
                    str(output_path)
                ]
                
                result = subprocess.run(ffmpeg_cmd, capture_output=True, text=True, timeout=300)
                
                if result.returncode == 0:
                    # Success - delete temp file
                    temp_video.unlink()
                    print(f"{t('annotation_audio_merged')}")
                else:
                    # Failed - restore original output
                    if temp_video.exists():
                        temp_video.rename(output_path)
                    print(f"{t('annotation_audio_failed')}")
            else:
                print(f"   ℹ️  Original video has no audio track")
                
        except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
            # ffmpeg not available or failed - keep video without audio
            print(f"   ⚠️  ffmpeg not available - video saved without audio")
            print(f"   💡 Install ffmpeg to preserve audio: sudo apt install ffmpeg")
        except Exception as e:
            # Any other error - keep video without audio
            print(f"   ⚠️  Could not merge audio: {e}")
        
        return {
            'input_video': str(video_path),
            'output_video': str(output_path),
            'total_frames': total_frames,
            'frames_processed': frames_processed,
            'birds_detected': total_birds_detected,
            'fps': fps
        }
