#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Main testing logic for Maniq with GPU support and comprehensive GPU statistics
"""

import os
import sys
import subprocess
import time
import threading
import json
import statistics
import traceback
from pathlib import Path
from datetime import datetime
from typing import List, Dict, Optional, Tuple

from .models import RenderResult, QualityTestResult, VideoInfo
from .resource_monitor import SystemResourceMonitor
from ..utils.video_analyzer import VideoAnalyzer
from ..utils.logging import get_logger
from ..i18n.translator import Translator


class ManimQualityStressTester:
    """Main class for Manim quality stress testing with GPU support"""
    
    def __init__(self, code_dir: str, output_dir: str = "manim_quality_output", 
                 log_output_dir: str = "manim_task_logs", launch_interval: float = 1.0,
                 language: str = 'en', use_gpu: bool = False, monitor_gpu: bool = False):
        self.code_dir = Path(code_dir)
        self.output_dir = Path(output_dir)
        self.log_output_dir = Path(log_output_dir)
        self.launch_interval = launch_interval
        self.translator = Translator(language)
        self.logger = get_logger()
        self.use_gpu = use_gpu
        self.monitor_gpu = monitor_gpu
        
        # Initialize GPU monitoring if requested
        self.gpu_monitor = None
        self.gpu_info = ""
        if self.monitor_gpu:
            try:
                from ..utils.gpu_monitor import GPUMonitor
                self.gpu_monitor = GPUMonitor()
                if self.gpu_monitor.nvml_available:
                    self.gpu_info = self.gpu_monitor.get_gpu_info()
                    self.logger.info(f"GPU monitoring enabled: {self.gpu_info}")
                else:
                    self.logger.warning("GPU monitoring requested but not available. GPU monitoring disabled.")
                    self.monitor_gpu = False
            except ImportError:
                self.logger.warning("GPU monitoring requested but pynvml not installed. GPU monitoring disabled.")
                self.monitor_gpu = False
        
        self.output_dir.mkdir(exist_ok=True)
        self.log_output_dir.mkdir(exist_ok=True)
        
        self.manim_files = sorted(list(self.code_dir.glob("*.py")))
        if not self.manim_files:
            raise ValueError(self.translator.get('no_py_files', code_dir=code_dir))
        
        self.logger.info(self.translator.get('found_files', count=len(self.manim_files)))
        self.logger.info(self.translator.get('sample_files', files=[f.name for f in self.manim_files[:5]]))
        self.logger.info(self.translator.get('task_interval', interval=launch_interval))
        
        # Quality configurations with GPU support
        base_flags = {
            'low': '-ql',
            'medium': '-qm', 
            'high': '-qh',
            '2k': '-qp',
            '4k': '-qk'
        }
        
        self.quality_configs = {}
        for quality, flag in base_flags.items():
            if self.use_gpu:
                # GPU rendering requires specific flags
                self.quality_configs[quality] = {
                    'flag': flag,
                    'description': self.translator.translations.get(f'{quality}_desc_gpu', 
                        f"{self.translator.translations.get(f'{quality}_desc', f'{quality.upper()} quality')} (GPU)"
                    ),
                    'gpu': True
                }
            else:
                self.quality_configs[quality] = {
                    'flag': flag,
                    'description': self.translator.translations.get(f'{quality}_desc', f'{quality.upper()} quality'),
                    'gpu': False
                }
        
        self.resource_monitor = SystemResourceMonitor()
        self.completed_tasks = []
        
        for quality in self.quality_configs:
            (self.log_output_dir / quality).mkdir(exist_ok=True)
    
    def _build_manim_command(self, file_path: Path, quality: str, output_path: Path) -> List[str]:
        """Build the appropriate Manim command based on CPU/GPU mode"""
        cmd = ["manim"]
        
        # Add quality flag
        cmd.append(self.quality_configs[quality]['flag'])
        
        if self.use_gpu:
            # GPU rendering requires specific flags
            cmd.extend([
                "--renderer=opengl",
                "--write_to_movie",
                "--disable_caching", 
                "--flush_cache"
            ])
        else:
            # CPU rendering
            cmd.append("--disable_caching")
        
        # Add output directory and file
        cmd.extend(["--media_dir", str(output_path)])
        cmd.append(str(file_path))
        
        return cmd
    
    def render_single_file(self, file_path: Path, quality: str, task_id: int) -> RenderResult:
        """Render single Manim file with CPU/GPU support"""
        start_time = time.time()
        output_subdir = f"{quality}_{task_id}"
        output_path = self.output_dir / output_subdir
        output_path.mkdir(exist_ok=True)
        
        # Get system resources (and GPU if monitoring)
        start_resources = self.resource_monitor.get_system_resources()
        start_gpu_usage = 0.0
        start_gpu_memory_usage = 0.0
        if self.gpu_monitor and self.monitor_gpu:
            start_gpu_usage, start_gpu_memory_usage = self.gpu_monitor.get_gpu_usage()
        
        # Build command
        cmd = self._build_manim_command(file_path, quality, output_path)
        
        task_log_file = self.log_output_dir / quality / f"task_{task_id:03d}.log"
        
        try:
            self.logger.info(self.translator.get('starting_task', quality=quality.upper(), task_id=task_id+1, filename=file_path.name))
            
            with open(task_log_file, 'w', encoding='utf-8') as log_f:
                log_f.write(f"Task ID: {task_id}\n")
                log_f.write(f"Quality: {quality} ({self.quality_configs[quality]['description']})\n")
                log_f.write(f"Render mode: {'GPU (OpenGL)' if self.use_gpu else 'CPU'}\n")
                log_f.write(f"File path: {file_path}\n")
                log_f.write(f"Command: {' '.join(cmd)}\n")
                log_f.write(f"Start time: {datetime.fromtimestamp(start_time).strftime('%Y-%m-%d %H:%M:%S')}\n")
                log_f.write(f"Initial CPU resources: CPU={start_resources['cpu_percent']:.1f}%, "
                           f"Memory={start_resources['memory_percent']:.1f}%, "
                           f"Available memory={start_resources['memory_available_gb']:.2f}GB\n")
                if self.monitor_gpu:
                    log_f.write(f"Initial GPU usage: {start_gpu_usage:.1f}%\n")
                    log_f.write(f"Initial GPU memory usage: {start_gpu_memory_usage:.1f}%\n")
                log_f.write("-" * 80 + "\n")
                log_f.write("Standard output and error output:\n")
                log_f.write("-" * 80 + "\n")
            
            process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                bufsize=1,
                universal_newlines=True
            )
            
            stdout_lines = []
            stderr_lines = []
            
            def read_stdout():
                for line in iter(process.stdout.readline, ''):
                    stdout_lines.append(line)
                    with open(task_log_file, 'a', encoding='utf-8') as log_f:
                        log_f.write(f"STDOUT: {line}")
                    self.logger.debug(f"[{quality.upper()} #{task_id}] STDOUT: {line.strip()}")
            
            def read_stderr():
                for line in iter(process.stderr.readline, ''):
                    stderr_lines.append(line)
                    with open(task_log_file, 'a', encoding='utf-8') as log_f:
                        log_f.write(f"STDERR: {line}")
                    self.logger.debug(f"[{quality.upper()} #{task_id}] STDERR: {line.strip()}")
            
            stdout_thread = threading.Thread(target=read_stdout)
            stderr_thread = threading.Thread(target=read_stderr)
            stdout_thread.daemon = True
            stderr_thread.daemon = True
            stdout_thread.start()
            stderr_thread.start()
            
            # GPU rendering may take longer, especially for high quality
            timeout_duration = 1800 if self.use_gpu else 1200
            
            try:
                process.wait(timeout=timeout_duration)
                timeout_occurred = False
            except subprocess.TimeoutExpired:
                process.kill()
                process.wait()
                timeout_occurred = True
                self.logger.warning(self.translator.get('reached_max_duration', duration=timeout_duration))
            
            stdout_thread.join(timeout=5)
            stderr_thread.join(timeout=5)
            
            end_time = time.time()
            end_resources = self.resource_monitor.get_system_resources()
            end_gpu_usage = 0.0
            end_gpu_memory_usage = 0.0
            if self.gpu_monitor and self.monitor_gpu:
                end_gpu_usage, end_gpu_memory_usage = self.gpu_monitor.get_gpu_usage()
            
            stdout_content = ''.join(stdout_lines)
            stderr_content = ''.join(stderr_lines)
            
            success = process.returncode == 0 and not timeout_occurred
            
            # Get video information
            video_info = None
            if success:
                video_file = VideoAnalyzer.find_rendered_video(output_path)
                if video_file:
                    video_info = VideoAnalyzer.get_video_info(video_file)
                    self.logger.info(f"[{quality.upper()} #{task_id}] Video: {video_file.name}, "
                                   f"Size: {video_info.file_size_mb:.2f}MB, "
                                   f"Duration: {video_info.duration_seconds:.2f}s")
            
            # Estimate resource usage
            estimated_cpu_usage = (start_resources['cpu_percent'] + end_resources['cpu_percent']) / 2
            estimated_gpu_usage = (start_gpu_usage + end_gpu_usage) / 2 if self.monitor_gpu else 0.0
            estimated_gpu_memory_usage = (start_gpu_memory_usage + end_gpu_memory_usage) / 2 if self.monitor_gpu else 0.0
            
            with open(task_log_file, 'a', encoding='utf-8') as log_f:
                log_f.write("-" * 80 + "\n")
                log_f.write(f"End time: {datetime.fromtimestamp(end_time).strftime('%Y-%m-%d %H:%M:%S')}\n")
                log_f.write(f"Duration: {end_time - start_time:.2f} seconds\n")
                log_f.write(f"Return code: {process.returncode}\n")
                log_f.write(f"Timeout: {timeout_occurred}\n")
                log_f.write(f"Success: {success}\n")
                log_f.write(f"Estimated CPU usage: {estimated_cpu_usage:.1f}%\n")
                if self.monitor_gpu:
                    log_f.write(f"Estimated GPU usage: {estimated_gpu_usage:.1f}%\n")
                    log_f.write(f"Estimated GPU memory usage: {estimated_gpu_memory_usage:.1f}%\n")
                if video_info:
                    log_f.write(f"File size: {video_info.file_size_mb:.2f} MB\n")
                    log_f.write(f"Video duration: {video_info.duration_seconds:.2f} seconds\n")
                    log_f.write(f"Resolution: {video_info.resolution}\n")
                log_f.write(f"Final CPU resources: CPU={end_resources['cpu_percent']:.1f}%, "
                           f"Memory={end_resources['memory_percent']:.1f}%, "
                           f"Available memory={end_resources['memory_available_gb']:.2f}GB\n")
                if self.monitor_gpu:
                    log_f.write(f"Final GPU usage: {end_gpu_usage:.1f}%\n")
                    log_f.write(f"Final GPU memory usage: {end_gpu_memory_usage:.1f}%\n")
            
            if success:
                self.logger.info(f"[{quality.upper()} #{task_id}] Task completed successfully "
                               f"(Duration: {end_time - start_time:.2f}s, CPU: {estimated_cpu_usage:.1f}%)")
                if self.monitor_gpu:
                    self.logger.info(f"[{quality.upper()} #{task_id}] GPU usage: {estimated_gpu_usage:.1f}%, "
                                   f"GPU memory: {estimated_gpu_memory_usage:.1f}%")
            else:
                error_msg = stderr_content if stderr_content else "Unknown error"
                self.logger.error(f"[{quality.upper()} #{task_id}] Task failed - Return code: {process.returncode}, "
                                f"Timeout: {timeout_occurred}, Error: {error_msg[:200]}...")
            
            render_result = RenderResult(
                file_name=file_path.name,
                quality=quality,
                task_id=task_id,
                start_time=start_time,
                end_time=end_time,
                duration=end_time - start_time,
                success=success,
                returncode=process.returncode,
                timeout=timeout_occurred,
                cpu_usage_start=start_resources['cpu_percent'],
                cpu_usage_end=end_resources['cpu_percent'],
                memory_usage_start=start_resources['memory_percent'],
                memory_usage_end=end_resources['memory_percent'],
                memory_available_start=start_resources['memory_available_gb'],
                memory_available_end=end_resources['memory_available_gb'],
                stdout=stdout_content,
                stderr=stderr_content,
                command=cmd,
                video_info=video_info,
                estimated_cpu_usage=estimated_cpu_usage,
                estimated_gpu_usage=estimated_gpu_usage,
                gpu_usage_start=start_gpu_usage,
                gpu_usage_end=end_gpu_usage,
                gpu_memory_usage_start=start_gpu_memory_usage,
                gpu_memory_usage_end=end_gpu_memory_usage,
                estimated_gpu_memory_usage=estimated_gpu_memory_usage
            )
            
            return render_result
            
        except Exception as e:
            end_time = time.time()
            end_resources = self.resource_monitor.get_system_resources()
            end_gpu_usage = 0.0
            end_gpu_memory_usage = 0.0
            if self.gpu_monitor and self.monitor_gpu:
                end_gpu_usage, end_gpu_memory_usage = self.gpu_monitor.get_gpu_usage()
            error_traceback = traceback.format_exc()
            
            with open(task_log_file, 'a', encoding='utf-8') as log_f:
                log_f.write(f"Exception: {str(e)}\n")
                log_f.write(f"Traceback:\n{error_traceback}\n")
            
            self.logger.error(f"[{quality.upper()} #{task_id}] Task execution exception: {e}")
            
            render_result = RenderResult(
                file_name=file_path.name,
                quality=quality,
                task_id=task_id,
                start_time=start_time,
                end_time=end_time,
                duration=end_time - start_time,
                success=False,
                returncode=-1,
                timeout=False,
                cpu_usage_start=start_resources['cpu_percent'],
                cpu_usage_end=end_resources['cpu_percent'],
                memory_usage_start=start_resources['memory_percent'],
                memory_usage_end=end_resources['memory_percent'],
                memory_available_start=start_resources['memory_available_gb'],
                memory_available_end=end_resources['memory_available_gb'],
                stdout="",
                stderr=str(e) + "\n" + error_traceback,
                command=cmd,
                estimated_cpu_usage=(start_resources['cpu_percent'] + end_resources['cpu_percent']) / 2,
                estimated_gpu_usage=(start_gpu_usage + end_gpu_usage) / 2 if self.monitor_gpu else 0.0,
                gpu_usage_start=start_gpu_usage,
                gpu_usage_end=end_gpu_usage,
                gpu_memory_usage_start=start_gpu_memory_usage,
                gpu_memory_usage_end=end_gpu_memory_usage,
                estimated_gpu_memory_usage=(start_gpu_memory_usage + end_gpu_memory_usage) / 2 if self.monitor_gpu else 0.0
            )
            
            return render_result
    
    def can_start_new_task(self, completed_tasks: List[RenderResult], current_cpu_usage: float) -> bool:
        """Intelligent check if new task can be started (with GPU support)"""
        if not completed_tasks:
            return True
        
        # Check CPU resources
        current_resources = self.resource_monitor.get_system_resources()
        current_cpu = current_resources['cpu_percent']
        current_memory = current_resources['memory_percent']
        
        if current_cpu > 90 or current_memory > 90:
            self.logger.warning(self.translator.get('waiting_resources'))
            return False
        
        # Check GPU resources if monitoring
        if self.gpu_monitor and self.monitor_gpu:
            current_gpu, current_gpu_memory = self.gpu_monitor.get_gpu_usage()
            if current_gpu > 90 or current_gpu_memory > 90:
                self.logger.warning(self.translator.get('waiting_resources'))
                return False
        
        # Calculate average resource usage from successful tasks
        successful_tasks = [t for t in completed_tasks if t.success]
        if not successful_tasks:
            avg_cpu_usage = 20.0
            avg_gpu_usage = 10.0 if self.monitor_gpu else 0.0
            avg_gpu_memory_usage = 10.0 if self.monitor_gpu else 0.0
        else:
            avg_cpu_usage = statistics.mean([t.estimated_cpu_usage for t in successful_tasks])
            if self.monitor_gpu:
                avg_gpu_usage = statistics.mean([t.estimated_gpu_usage for t in successful_tasks])
                avg_gpu_memory_usage = statistics.mean([t.estimated_gpu_memory_usage for t in successful_tasks])
            else:
                avg_gpu_usage = 0.0
                avg_gpu_memory_usage = 0.0
        
        # Check CPU resources
        remaining_cpu = 100 - current_cpu
        required_cpu = avg_cpu_usage + 5.0
        cpu_ok = remaining_cpu >= required_cpu
        
        # Check GPU resources
        gpu_ok = True
        if self.monitor_gpu:
            current_gpu, current_gpu_memory = self.gpu_monitor.get_gpu_usage()
            remaining_gpu = 100 - current_gpu
            remaining_gpu_memory = 100 - current_gpu_memory
            required_gpu = avg_gpu_usage + 5.0
            required_gpu_memory = avg_gpu_memory_usage + 5.0
            gpu_ok = remaining_gpu >= required_gpu and remaining_gpu_memory >= required_gpu_memory
        
        can_start = cpu_ok and gpu_ok
        
        if not can_start:
            self.logger.info(self.translator.get('waiting_resources'))
        
        return can_start
    
    def _calculate_max_concurrent(self, results: List[RenderResult]) -> int:
        """Calculate maximum concurrent tasks"""
        if not results:
            return 0
        
        events = []
        for result in results:
            events.append((result.start_time, 1))
            events.append((result.end_time, -1))
        
        events.sort()
        
        current_concurrent = 0
        max_concurrent = 0
        
        for _, delta in events:
            current_concurrent += delta
            max_concurrent = max(max_concurrent, current_concurrent)
        
        return max_concurrent
    
    def _render_task_wrapper(self, file_path: Path, quality: str, task_id: int, results_list: List[RenderResult]):
        """Wrapper for render task"""
        result = self.render_single_file(file_path, quality, task_id)
        results_list.append(result)
        status = "success" if result.success else "failed"
        self.logger.info(f"[{quality.upper()} #{task_id}] Task {status} (Duration: {result.duration:.2f}s)")
    
    def test_quality_level(self, quality: str, max_test_duration: int = 1800) -> QualityTestResult:
        """Test specified quality level with intelligent resource management"""
        self.logger.info(f"\n{'='*80}")
        self.logger.info(self.translator.get('starting_test', description=self.quality_configs[quality]['description']))
        self.logger.info(self.translator.get('task_interval', interval=self.launch_interval))
        self.logger.info(self.translator.get('intelligent_resource'))
        render_type = "GPU (OpenGL)" if self.use_gpu else "CPU"
        self.logger.info(f"Render type: {render_type}")
        self.logger.info(f"{'='*80}")
        
        # Reset monitoring data
        self.resource_monitor.clear_monitor_data()
        self.resource_monitor.start_monitoring()
        self.completed_tasks = []
        
        start_test_time = time.time()
        render_results = []
        task_id = 0
        consecutive_failures = 0
        max_consecutive_failures = 3
        last_task_start_time = 0
        
        try:
            while True:
                current_time = time.time()
                elapsed_time = current_time - start_test_time
                
                if elapsed_time > max_test_duration:
                    self.logger.info(self.translator.get('reached_max_duration', duration=max_test_duration))
                    break
                
                if not self.can_start_new_task(self.completed_tasks, 
                                             self.resource_monitor.get_system_resources()['cpu_percent']):
                    self.logger.info(self.translator.get('waiting_resources'))
                    time.sleep(5)
                    continue
                
                if task_id >= len(self.manim_files):
                    file_to_render = self.manim_files[task_id % len(self.manim_files)]
                else:
                    file_to_render = self.manim_files[task_id]
                
                self.logger.info(self.translator.get('starting_task', quality=quality.upper(), task_id=task_id+1, filename=file_to_render.name))
                
                render_thread = threading.Thread(
                    target=self._render_task_wrapper,
                    args=(file_to_render, quality, task_id, render_results)
                )
                render_thread.daemon = True
                render_thread.start()
                
                task_id += 1
                last_task_start_time = current_time
                time.sleep(self.launch_interval)
                
                self.completed_tasks = [r for r in render_results if r.end_time > 0]
                
                recent_results = [r for r in self.completed_tasks if r.end_time > current_time - 60]
                if recent_results:
                    recent_failures = sum(1 for r in recent_results if not r.success)
                    if recent_failures >= max_consecutive_failures:
                        self.logger.info(self.translator.get('reached_max_duration', duration=max_test_duration))
                        break
                
                active_tasks = len([r for r in render_results if r.end_time == 0])
                if (current_time - last_task_start_time > max(120, self.launch_interval * 10) and 
                    active_tasks > 10):
                    self.logger.warning(self.translator.get('reached_max_duration', duration=max_test_duration))
                    break
            
            self.logger.info(self.translator.get('waiting_completion', quality=quality.upper()))
            wait_start = time.time()
            max_wait_time = 900 if self.use_gpu else 600  # GPU may take longer
            while time.time() - wait_start < max_wait_time:
                completed_count = sum(1 for r in render_results if r.end_time > 0)
                if completed_count == len(render_results):
                    break
                remaining = len(render_results) - completed_count
                self.logger.info(self.translator.get('tasks_remaining', quality=quality.upper(), remaining=remaining))
                time.sleep(10)
            
            self.resource_monitor.stop_monitoring()
            
            successful_results = [r for r in render_results if r.success]
            durations = [r.duration for r in successful_results]
            
            video_durations = []
            file_sizes = []
            for r in successful_results:
                if r.video_info:
                    if r.video_info.duration_seconds > 0:
                        video_durations.append(r.video_info.duration_seconds)
                    if r.video_info.file_size_mb > 0:
                        file_sizes.append(r.video_info.file_size_mb)
            
            if durations:
                avg_duration = statistics.mean(durations)
                min_duration = min(durations)
                max_duration = max(durations)
                median_duration = statistics.median(durations)
                std_duration = statistics.stdev(durations) if len(durations) > 1 else 0
            else:
                avg_duration = min_duration = max_duration = median_duration = std_duration = 0
            
            if video_durations:
                avg_video_duration = statistics.mean(video_durations)
                min_video_duration = min(video_durations)
                max_video_duration = max(video_durations)
            else:
                avg_video_duration = min_video_duration = max_video_duration = 0
            
            if file_sizes:
                avg_file_size = statistics.mean(file_sizes)
                min_file_size = min(file_sizes)
                max_file_size = max(file_sizes)
            else:
                avg_file_size = min_file_size = max_file_size = 0
            
            successful_count = len(successful_results)
            failed_count = len(render_results) - successful_count
            success_rate = successful_count / len(render_results) if render_results else 0
            
            max_concurrent = self._calculate_max_concurrent(render_results)
            test_duration = time.time() - start_test_time
            tasks_per_second = len(render_results) / test_duration if test_duration > 0 else 0
            
            cpu_usage_history = [r.estimated_cpu_usage for r in successful_results if r.estimated_cpu_usage > 0]
            gpu_usage_history = []
            gpu_memory_usage_history = []
            if self.monitor_gpu:
                gpu_usage_history = [r.estimated_gpu_usage for r in successful_results if r.estimated_gpu_usage > 0]
                gpu_memory_usage_history = [r.estimated_gpu_memory_usage for r in successful_results if r.estimated_gpu_memory_usage > 0]
            
            result = QualityTestResult(
                quality=quality,
                max_concurrent_tasks=max_concurrent,
                total_tasks_started=len(render_results),
                successful_tasks=successful_count,
                failed_tasks=failed_count,
                success_rate=success_rate,
                task_durations=durations,
                avg_duration=avg_duration,
                min_duration=min_duration,
                max_duration=max_duration,
                median_duration=median_duration,
                std_duration=std_duration,
                video_durations=video_durations,
                avg_video_duration=avg_video_duration,
                min_video_duration=min_video_duration,
                max_video_duration=max_video_duration,
                video_file_sizes=file_sizes,
                avg_file_size=avg_file_size,
                min_file_size=min_file_size,
                max_file_size=max_file_size,
                resource_usage=self.resource_monitor.get_monitor_data(),
                final_system_resources=self.resource_monitor.get_system_resources(),
                test_duration=test_duration,
                tasks_per_second=tasks_per_second,
                individual_results=render_results,
                cpu_usage_history=cpu_usage_history,
                gpu_usage_history=gpu_usage_history,
                gpu_memory_usage_history=gpu_memory_usage_history,
                use_gpu=self.use_gpu,
                gpu_info=self.gpu_info
            )
            
            self.logger.info(self.translator.get('test_completed', quality=quality.upper()))
            self.logger.info(self.translator.get('total_tasks', count=result.total_tasks_started))
            self.logger.info(self.translator.get('successful_tasks', count=result.successful_tasks))
            self.logger.info(self.translator.get('max_concurrent', count=result.max_concurrent_tasks))
            self.logger.info(self.translator.get('avg_render_duration', duration=result.avg_duration))
            self.logger.info(self.translator.get('avg_video_duration', duration=result.avg_video_duration))
            self.logger.info(self.translator.get('avg_file_size', size=result.avg_file_size))
            self.logger.info(self.translator.get('success_rate', rate=result.success_rate))
            
            return result
            
        except KeyboardInterrupt:
            self.logger.info(self.translator.get('test_interrupted'))
            self.resource_monitor.stop_monitoring()
            raise
        except Exception as e:
            self.logger.error(self.translator.get('critical_error', error=e))
            self.logger.error(f"[{quality.upper()}] Traceback:\n{traceback.format_exc()}")
            self.resource_monitor.stop_monitoring()
            raise
    
    def run_selected_tests(self, selected_qualities: List[str], max_duration_per_test: int = 1800) -> Dict[str, QualityTestResult]:
        """Run selected quality level tests"""
        results = {}
        
        valid_qualities = set(self.quality_configs.keys())
        invalid_qualities = set(selected_qualities) - valid_qualities
        if invalid_qualities:
            self.logger.warning(f"Invalid quality levels: {invalid_qualities}")
            selected_qualities = [q for q in selected_qualities if q in valid_qualities]
        
        if not selected_qualities:
            self.logger.error(self.translator.get('all_tests_failed'))
            return results
        
        self.logger.info(self.translator.get('testing_qualities', qualities=selected_qualities))
        
        for quality in selected_qualities:
            try:
                self.logger.info(f"\n{'='*80}")
                self.logger.info(self.translator.get('preparing_test', description=self.quality_configs[quality]['description']))
                self.logger.info(f"{'='*80}")
                
                self.logger.info(self.translator.get('waiting_recovery'))
                for i in range(30, 0, -1):
                    if i % 10 == 0:
                        resources = self.resource_monitor.get_system_resources()
                        self.logger.info(self.translator.get('countdown_resources', seconds=i, cpu=resources['cpu_percent'], memory=resources['memory_percent']))
                        if self.gpu_monitor and self.monitor_gpu:
                            gpu_usage, gpu_memory_usage = self.gpu_monitor.get_gpu_usage()
                            self.logger.info(f"GPU usage: {gpu_usage:.1f}%, GPU memory: {gpu_memory_usage:.1f}%")
                    time.sleep(1)
                
                result = self.test_quality_level(quality, max_duration_per_test)
                results[quality] = result
                
            except KeyboardInterrupt:
                self.logger.info(self.translator.get('test_interrupted'))
                break
            except Exception as e:
                self.logger.error(f"{quality} quality test failed: {e}")
                continue
        
        return results
    
    def generate_report(self, results: Dict[str, QualityTestResult]) -> str:
        """Generate detailed test report with GPU statistics"""
        from ..utils.system_info import get_system_info
        
        report_lines = []
        report_lines.append("=" * 130)
        report_lines.append(self.translator.get('report_title'))
        report_lines.append("=" * 130)
        report_lines.append(self.translator.get('test_time', time=datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
        report_lines.append(self.translator.get('test_files', count=len(self.manim_files)))
        report_lines.append(self.translator.get('code_directory', path=self.code_dir))
        render_type = "GPU (OpenGL)" if self.use_gpu else "CPU"
        report_lines.append(f"Render type: {render_type}")
        report_lines.append(self.translator.get('task_launch_interval', interval=self.launch_interval))
        report_lines.append("")
        
        # System information
        system_info = get_system_info()
        report_lines.append(self.translator.get('system_information'))
        report_lines.append(self.translator.get('cpu_cores', physical=system_info['cpu_physical'], logical=system_info['cpu_logical']))
        report_lines.append(self.translator.get('total_memory', memory=system_info['total_memory_gb']))
        report_lines.append(self.translator.get('available_disk', disk=system_info['available_disk_gb']))
        if self.use_gpu and self.gpu_info:
            report_lines.append(f"  GPU: {self.gpu_info}")
        report_lines.append("")
        
        all_qualities = ['low', 'medium', 'high', '2k', '4k']
        quality_names = {
            'low': self.translator.translations.get('low_name', 'LOW'),
            'medium': self.translator.translations.get('medium_name', 'MEDIUM'),
            'high': self.translator.translations.get('high_name', 'HIGH'),
            '2k': self.translator.translations.get('2k_name', '2K'),
            '4k': self.translator.translations.get('4k_name', '4K'),
        }
        
        for quality in all_qualities:
            if quality not in results:
                continue
                
            result = results[quality]
            desc = self.quality_configs[quality]['description']
            
            report_lines.append(self.translator.get('test_results', description=desc))
            report_lines.append("-" * 90)
            report_lines.append(self.translator.get('max_concurrent_tasks', count=result.max_concurrent_tasks))
            report_lines.append(self.translator.get('total_tasks_started', count=result.total_tasks_started))
            report_lines.append(self.translator.get('successful_tasks_report', count=result.successful_tasks))
            report_lines.append(self.translator.get('failed_tasks', count=result.failed_tasks))
            report_lines.append(self.translator.get('success_rate_report', rate=result.success_rate))
            report_lines.append("")
            
            if result.task_durations:
                report_lines.append(self.translator.get('render_duration_stats'))
                report_lines.append(self.translator.get('average', value=result.avg_duration))
                report_lines.append(self.translator.get('minimum', value=result.min_duration))
                report_lines.append(self.translator.get('maximum', value=result.max_duration))
                report_lines.append(self.translator.get('median', value=result.median_duration))
                report_lines.append(self.translator.get('std_dev', value=result.std_duration))
                report_lines.append("")
            
            if result.video_durations:
                report_lines.append(self.translator.get('video_duration_stats'))
                report_lines.append(self.translator.get('average', value=result.avg_video_duration))
                report_lines.append(self.translator.get('minimum', value=result.min_video_duration))
                report_lines.append(self.translator.get('maximum', value=result.max_video_duration))
                report_lines.append("")
            
            if result.video_file_sizes:
                report_lines.append(self.translator.get('file_size_stats'))
                report_lines.append(self.translator.get('average', value=result.avg_file_size))
                report_lines.append(self.translator.get('minimum', value=result.min_file_size))
                report_lines.append(self.translator.get('maximum', value=result.max_file_size))
                report_lines.append("")
            
            if result.resource_usage:
                cpu_usage = [r['cpu_percent'] for r in result.resource_usage]
                memory_usage = [r['memory_percent'] for r in result.resource_usage]
                
                report_lines.append(self.translator.get('resource_usage_stats'))
                report_lines.append(self.translator.get('cpu_usage_stats', avg=statistics.mean(cpu_usage), peak=max(cpu_usage)))
                report_lines.append(self.translator.get('memory_usage_stats', avg=statistics.mean(memory_usage), peak=max(memory_usage)))
                
                # GPU usage statistics
                if result.use_gpu and result.gpu_usage_history:
                    gpu_usage = result.gpu_usage_history
                    gpu_memory_usage = result.gpu_memory_usage_history
                    
                    avg_gpu = statistics.mean(gpu_usage) if gpu_usage else 0.0
                    peak_gpu = max(gpu_usage) if gpu_usage else 0.0
                    avg_gpu_memory = statistics.mean(gpu_memory_usage) if gpu_memory_usage else 0.0
                    peak_gpu_memory = max(gpu_memory_usage) if gpu_memory_usage else 0.0
                    
                    report_lines.append(f"  GPU usage - Average: {avg_gpu:.1f}%, Peak: {peak_gpu:.1f}%")
                    report_lines.append(f"  GPU memory usage - Average: {avg_gpu_memory:.1f}%, Peak: {peak_gpu_memory:.1f}%")
                report_lines.append("")
            
            if result.cpu_usage_history:
                avg_cpu_per_task = statistics.mean(result.cpu_usage_history)
                report_lines.append(self.translator.get('avg_cpu_per_task', usage=avg_cpu_per_task))
                if result.use_gpu and result.gpu_usage_history:
                    avg_gpu_per_task = statistics.mean(result.gpu_usage_history)
                    avg_gpu_memory_per_task = statistics.mean(result.gpu_memory_usage_history)
                    report_lines.append(f"Average GPU usage per task: {avg_gpu_per_task:.1f}%")
                    report_lines.append(f"Average GPU memory usage per task: {avg_gpu_memory_per_task:.1f}%")
                report_lines.append("")
            
            report_lines.append(self.translator.get('detailed_logs_saved', path=self.log_output_dir / quality))
            report_lines.append("")
        
        report_lines.append(self.translator.get('performance_comparison'))
        
        # Create properly aligned table using the new utility
        from ..utils.text_utils import create_aligned_table
        
        headers = [
            self.translator.get('quality'),
            self.translator.get('max_concurrent_col'),
            self.translator.get('success_rate_col'),
            self.translator.get('avg_render_time_col'),
            self.translator.get('avg_video_duration_col'),
            self.translator.get('avg_file_size_col')
        ]
        
        # Prepare data rows
        data_rows = []
        for quality in all_qualities:
            if quality not in results:
                continue
            result = results[quality]
            quality_name = quality_names[quality]
            row = [
                quality_name,
                str(result.max_concurrent_tasks),
                f"{result.success_rate:.2%}",
                f"{result.avg_duration:.2f}",
                f"{result.avg_video_duration:.2f}",
                f"{result.avg_file_size:.2f}"
            ]
            data_rows.append(row)
        
        # Create aligned table (this will handle CJK characters properly)
        table = create_aligned_table(headers, data_rows)
        report_lines.append(table)
        report_lines.append("")
        
        report_lines.append(self.translator.get('resource_strategy'))
        report_lines.append(self.translator.get('strategy_point1'))
        report_lines.append(self.translator.get('strategy_point2'))
        report_lines.append(self.translator.get('strategy_point3'))
        report_lines.append(self.translator.get('strategy_point4'))
        report_lines.append("")
        
        report_lines.append(self.translator.get('recommendations'))
        if results:
            best_quality = max(results.keys(), key=lambda q: results[q].max_concurrent_tasks)
            best_quality_name = quality_names[best_quality]
            report_lines.append(self.translator.get('recommend_best_quality', quality=best_quality_name))
            
            qualities_with_data = [q for q in ['low', 'medium', 'high', '2k', '4k'] if q in results]
            if len(qualities_with_data) >= 2:
                first_q = qualities_with_data[0]
                last_q = qualities_with_data[-1]
                first_size = results[first_q].avg_file_size
                last_size = results[last_q].avg_file_size
                if first_size > 0 and last_size > 0:
                    size_ratio = last_size / first_size
                    first_name = quality_names[first_q]
                    last_name = quality_names[last_q]
                    report_lines.append(self.translator.get('recommend_file_size', 
                                                          high_quality=last_name, 
                                                          low_quality=first_name, 
                                                          ratio=size_ratio))
        
        report_lines.append("=" * 130)
        
        return "\n".join(report_lines)
    
    def generate_json_report(self, results: Dict[str, QualityTestResult]) -> str:
        """Generate JSON report"""
        from dataclasses import asdict
        
        json_results = {}
        for quality, result in results.items():
            json_results[quality] = asdict(result)
            if 'individual_results' in json_results[quality]:
                individual_results = []
                for r in json_results[quality]['individual_results']:
                    r_copy = r.copy()
                    if len(r_copy.get('stdout', '')) > 10000:
                        r_copy['stdout'] = r_copy['stdout'][:10000] + " [truncated...]"
                    if len(r_copy.get('stderr', '')) > 10000:
                        r_copy['stderr'] = r_copy['stderr'][:10000] + " [truncated...]"
                    individual_results.append(r_copy)
                json_results[quality]['individual_results'] = individual_results
            
            if 'resource_usage' in json_results[quality]:
                json_results[quality]['resource_usage'] = [
                    {k: v for k, v in r.items()} for r in json_results[quality]['resource_usage']
                ]
        
        return json.dumps(json_results, indent=2, ensure_ascii=False, default=str)
        