"""
T235: 4-Hour DJ Test
Prueba final DJ de 4 horas ininterrumpidas - MILESTONE FINAL
"""

import time
import threading
import json
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
from dataclasses import dataclass


@dataclass
class TestCheckpoint:
    """Punto de control del test."""
    timestamp: str
    elapsed_minutes: float
    status: str
    metrics: Dict[str, Any]


class FourHourDJTest:
    """
    Prueba DJ de 4 horas ininterrumpidas.
    
    T235: MILESTONE FINAL - Test completo de estabilidad y performance.
    """
    
    TEST_DURATION_HOURS = 4.0
    CHECKPOINT_INTERVAL_MINUTES = 15.0
    
    def __init__(self):
        self.duration = timedelta(hours=self.TEST_DURATION_HOURS)
        self.checkpoints: List[TestCheckpoint] = []
        self.running = False
        self.start_time: Optional[datetime] = None
        self.test_thread: Optional[threading.Thread] = None
        self.errors: List[Dict[str, Any]] = []
        
    def start_test(self, auto_generate_sets: bool = True) -> Dict[str, Any]:
        """
        Inicia prueba de 4 horas.
        
        Args:
            auto_generate_sets: Generar sets automáticamente durante la prueba
            
        Returns:
            Estado inicial del test
        """
        if self.running:
            return {'status': 'already_running'}
        
        self.running = True
        self.start_time = datetime.now()
        self.checkpoints = []
        self.errors = []
        
        # Iniciar thread de test
        self.test_thread = threading.Thread(
            target=self._run_test,
            args=(auto_generate_sets,),
            daemon=True
        )
        self.test_thread.start()
        
        return {
            'status': 'started',
            'test_id': f'4h_test_{self.start_time.strftime("%Y%m%d_%H%M%S")}',
            'duration_hours': self.TEST_DURATION_HOURS,
            'start_time': self.start_time.isoformat(),
            'estimated_end': (self.start_time + self.duration).isoformat(),
            'checkpoints_expected': int(self.TEST_DURATION_HOURS * 60 / self.CHECKPOINT_INTERVAL_MINUTES)
        }
    
    def _run_test(self, auto_generate_sets: bool):
        """Ejecuta el test de 4 horas."""
        checkpoint_count = 0
        
        while self.running:
            elapsed = datetime.now() - self.start_time
            
            # Verificar si terminó
            if elapsed >= self.duration:
                self._record_checkpoint('completed', elapsed)
                self.running = False
                break
            
            # Registrar checkpoint cada 15 minutos
            minutes_elapsed = elapsed.total_seconds() / 60
            expected_checkpoint = int(minutes_elapsed / self.CHECKPOINT_INTERVAL_MINUTES)
            
            if expected_checkpoint > checkpoint_count:
                checkpoint_count = expected_checkpoint
                self._record_checkpoint(f'checkpoint_{checkpoint_count}', elapsed)
            
            # Auto-generar sets si está habilitado (cada 30 min)
            if auto_generate_sets and int(minutes_elapsed) % 30 == 0 and int(minutes_elapsed) > 0:
                self._auto_generate_set(elapsed)
            
            # Verificar salud del sistema
            self._check_system_health(elapsed)
            
            # Esperar antes de siguiente iteración
            time.sleep(60)  # Chequeo cada minuto
    
    def _record_checkpoint(self, status: str, elapsed: timedelta):
        """Registra punto de control."""
        metrics = self._collect_metrics()
        
        checkpoint = TestCheckpoint(
            timestamp=datetime.now().isoformat(),
            elapsed_minutes=elapsed.total_seconds() / 60,
            status=status,
            metrics=metrics
        )
        
        self.checkpoints.append(checkpoint)
        
        print(f"[4H Test] Checkpoint {len(self.checkpoints)}: {status} "
              f"({checkpoint.elapsed_minutes:.1f} min)")
    
    def _collect_metrics(self) -> Dict[str, Any]:
        """Recolecta métricas actuales."""
        try:
            import psutil
            
            return {
                'cpu_percent': psutil.cpu_percent(interval=1),
                'memory_percent': psutil.virtual_memory().percent,
                'memory_available_mb': psutil.virtual_memory().available / 1024 / 1024,
                'disk_usage_percent': psutil.disk_usage('/').percent,
                'connections': len(psutil.net_connections()),
                'timestamp': datetime.now().isoformat()
            }
        except:
            return {'error': 'psutil not available'}
    
    def _auto_generate_set(self, elapsed: timedelta):
        """Genera set automáticamente."""
        print(f"[4H Test] Auto-generating set at {elapsed.total_seconds() / 60:.0f} minutes")
        
        # En producción, llamaría al generador
        # Por ahora, simulamos
        time.sleep(2)  # Simular generación
    
    def _check_system_health(self, elapsed: timedelta):
        """Verifica salud del sistema."""
        metrics = self._collect_metrics()
        
        # Verificar CPU
        if metrics.get('cpu_percent', 0) > 90:
            self._record_error('high_cpu', metrics['cpu_percent'], elapsed)
        
        # Verificar memoria
        if metrics.get('memory_percent', 0) > 95:
            self._record_error('high_memory', metrics['memory_percent'], elapsed)
        
        # Verificar espacio en disco
        if metrics.get('disk_usage_percent', 0) > 95:
            self._record_error('low_disk_space', metrics['disk_usage_percent'], elapsed)
    
    def _record_error(self, error_type: str, value: float, elapsed: timedelta):
        """Registra error durante el test."""
        self.errors.append({
            'type': error_type,
            'value': value,
            'elapsed_minutes': elapsed.total_seconds() / 60,
            'timestamp': datetime.now().isoformat()
        })
        
        print(f"[4H Test] ERROR: {error_type} = {value} at {elapsed.total_seconds() / 60:.0f} min")
    
    def get_status(self) -> Dict[str, Any]:
        """Obtiene estado actual del test."""
        if not self.running and not self.checkpoints:
            return {'status': 'not_started'}
        
        if not self.running and self.checkpoints:
            return self._get_final_report()
        
        elapsed = datetime.now() - self.start_time
        progress = min(100, (elapsed.total_seconds() / self.duration.total_seconds()) * 100)
        
        return {
            'status': 'running',
            'start_time': self.start_time.isoformat() if self.start_time else None,
            'elapsed_minutes': elapsed.total_seconds() / 60,
            'remaining_minutes': (self.duration.total_seconds() - elapsed.total_seconds()) / 60,
            'progress_percent': progress,
            'checkpoints_completed': len(self.checkpoints),
            'errors_count': len(self.errors),
            'current_metrics': self._collect_metrics()
        }
    
    def _get_final_report(self) -> Dict[str, Any]:
        """Genera reporte final del test."""
        total_duration = self.checkpoints[-1].elapsed_minutes if self.checkpoints else 0
        
        # Analizar checkpoints
        cpu_values = [c.metrics.get('cpu_percent', 0) for c in self.checkpoints if 'cpu_percent' in c.metrics]
        memory_values = [c.metrics.get('memory_percent', 0) for c in self.checkpoints if 'memory_percent' in c.metrics]
        
        return {
            'status': 'completed',
            'test_id': f'4h_test_{self.start_time.strftime("%Y%m%d_%H%M%S")}' if self.start_time else 'unknown',
            'start_time': self.start_time.isoformat() if self.start_time else None,
            'end_time': self.checkpoints[-1].timestamp if self.checkpoints else None,
            'total_duration_minutes': total_duration,
            'checkpoints_total': len(self.checkpoints),
            'errors_total': len(self.errors),
            'performance_summary': {
                'cpu_avg': sum(cpu_values) / len(cpu_values) if cpu_values else 0,
                'cpu_max': max(cpu_values) if cpu_values else 0,
                'memory_avg': sum(memory_values) / len(memory_values) if memory_values else 0,
                'memory_max': max(memory_values) if memory_values else 0
            },
            'errors': self.errors,
            'grade': self._calculate_grade(),
            'passed': len(self.errors) < 5 and total_duration >= self.TEST_DURATION_HOURS * 60 * 0.95
        }
    
    def _calculate_grade(self) -> str:
        """Calcula calificación del test."""
        if not self.checkpoints:
            return 'F'
        
        error_score = max(0, 100 - len(self.errors) * 10)
        completion_score = (self.checkpoints[-1].elapsed_minutes / (self.TEST_DURATION_HOURS * 60)) * 100
        
        total_score = (error_score + completion_score) / 2
        
        if total_score >= 95:
            return 'A+'
        elif total_score >= 90:
            return 'A'
        elif total_score >= 80:
            return 'B'
        elif total_score >= 70:
            return 'C'
        elif total_score >= 60:
            return 'D'
        else:
            return 'F'
    
    def stop_test(self) -> Dict[str, Any]:
        """Detiene el test."""
        if not self.running:
            return {'status': 'not_running'}
        
        self.running = False
        
        if self.test_thread:
            self.test_thread.join(timeout=10)
        
        return self._get_final_report()
    
    def export_report(self, filepath: str) -> Dict[str, Any]:
        """Exporta reporte a archivo."""
        report = self._get_final_report()
        
        with open(filepath, 'w') as f:
            json.dump(report, f, indent=2)
        
        return {
            'exported': True,
            'filepath': filepath,
            'report': report
        }


# Instancia global
_4h_test_instance: Optional[FourHourDJTest] = None

def start_4hour_dj_test(auto_generate_sets: bool = True) -> Dict[str, Any]:
    """
    T235: Inicia prueba DJ de 4 horas ininterrumpidas.
    
    Args:
        auto_generate_sets: Generar sets automáticamente durante la prueba
        
    Returns:
        Estado inicial del test
    """
    global _4h_test_instance
    
    if _4h_test_instance is None:
        _4h_test_instance = FourHourDJTest()
    
    return _4h_test_instance.start_test(auto_generate_sets)


def get_4hour_test_status() -> Dict[str, Any]:
    """Obtiene estado del test de 4 horas."""
    global _4h_test_instance
    
    if _4h_test_instance is None:
        return {'status': 'not_initialized'}
    
    return _4h_test_instance.get_status()


def stop_4hour_test() -> Dict[str, Any]:
    """Detiene el test de 4 horas."""
    global _4h_test_instance
    
    if _4h_test_instance is None:
        return {'status': 'not_running'}
    
    return _4h_test_instance.stop_test()


if __name__ == '__main__':
    # Test de 4 horas (versión corta para prueba)
    print("T235: Starting 4-Hour DJ Test (MILESTONE FINAL)")
    print("=" * 60)
    
    # Iniciar test
    result = start_4hour_dj_test(auto_generate_sets=False)
    print(f"\nTest Started:")
    print(json.dumps(result, indent=2))
    
    print("\nTest is running. Monitoring for 5 seconds...")
    time.sleep(5)
    
    # Obtener estado
    status = get_4hour_test_status()
    print(f"\nCurrent Status:")
    print(json.dumps(status, indent=2))
    
    # Detener test
    print("\nStopping test...")
    final = stop_4hour_test()
    print(f"\nFinal Report:")
    print(json.dumps(final, indent=2))
    
    print("\n" + "=" * 60)
    print("T235: 4-Hour DJ Test Complete")
    print(f"Grade: {final.get('grade', 'N/A')}")
    print(f"Passed: {final.get('passed', False)}")
