import pytest
import asyncio
import time
import threading
from .bullmq_visibility import (
    bullmq_visibility_summary,
    bullmq_visibility_worker_summary,
    get_queue_health,
    bullmq_visibility_realtime,
    get_queue_stats
)
from .bullmq_sender import bullmq_sender
from .bullmq_consumer import consumer_bullmq

# Variables globales para tests
test_redis_url = "redis://192.168.1.163:6379"
test_queues = ["visibility_test_1", "visibility_test_2", "visibility_test_3"]

def visibility_test_worker(message):
    """Worker de prueba para tests de visibilidad."""
    print(f"Visibility test processing: {message}")
    if hasattr(visibility_test_worker, 'processed_messages'):
        visibility_test_worker.processed_messages.append(message)
    return {"status": "processed", "data": message}

def setup_test_environment():
    """Configura el entorno de prueba con mensajes en diferentes colas."""
    visibility_test_worker.processed_messages = []
    
    # Enviar mensajes a diferentes colas para tener datos de prueba
    for i, queue_name in enumerate(test_queues):
        # Enviar mensajes con diferentes prioridades y estados
        bullmq_sender(
            worker=queue_name,
            priority=5 + i,
            message={"test_id": f"test_{i}", "queue": queue_name}
        )
        
        # Enviar algunos mensajes con delay para testing
        if i == 1:
            bullmq_sender(
                worker=queue_name,
                priority=10,
                delay_sec=2,
                message={"test_id": f"delayed_{i}", "queue": queue_name}
            )

# =============================================================================
# TESTS PARA ESTADÍSTICAS BÁSICAS DE COLAS
# =============================================================================

def test_get_queue_stats_with_existing_queue():
    """Test para obtener estadísticas de una cola existente."""
    # Setup - enviar algunos mensajes
    queue_name = "visibility_stats_test"
    
    # Enviar mensajes de prueba
    job_id1 = bullmq_sender(worker=queue_name, priority=5, message={"test": "stats1"})
    job_id2 = bullmq_sender(worker=queue_name, priority=3, message={"test": "stats2"})
    
    assert job_id1 is not None, "Failed to send first test message"
    assert job_id2 is not None, "Failed to send second test message"
    
    # Test - obtener estadísticas
    async def get_stats():
        return await get_queue_stats(queue_name, test_redis_url)
    
    stats = asyncio.run(get_stats())
    
    # Verificaciones
    assert stats["queue_name"] == queue_name, "Queue name mismatch"
    assert "waiting" in stats, "Missing waiting count"
    assert "completed" in stats, "Missing completed count"
    assert "failed" in stats, "Missing failed count"
    assert "total_jobs" in stats, "Missing total jobs count"
    assert "status" in stats, "Missing status"
    assert "processing_rate" in stats, "Missing processing rate"
    
    print(f"✅ Queue stats test passed: {stats}")

def test_get_queue_stats_with_nonexistent_queue():
    """Test para manejar colas que no existen."""
    nonexistent_queue = "queue_that_does_not_exist_12345"
    
    async def get_stats():
        return await get_queue_stats(nonexistent_queue, test_redis_url)
    
    stats = asyncio.run(get_stats())
    
    # Verificar que maneja correctamente colas inexistentes
    assert stats["queue_name"] == nonexistent_queue
    assert stats["total_jobs"] == 0
    assert stats["waiting"] == 0
    assert stats["processing_rate"] == 0
    
    print("✅ Nonexistent queue handling test passed")

# =============================================================================
# TESTS PARA RESUMEN GENERAL DEL SISTEMA
# =============================================================================

def test_bullmq_visibility_summary_with_data():
    """Test para el resumen general con datos reales."""
    # Setup - crear datos de prueba
    setup_test_environment()
    time.sleep(1)  # Esperar que se procesen los mensajes
    
    # Test - obtener resumen
    summary = bullmq_visibility_summary(test_queues, test_redis_url)
    
    # Verificaciones básicas
    assert "total_queues" in summary, "Missing total_queues"
    assert "total_waiting" in summary, "Missing total_waiting"
    assert "total_completed" in summary, "Missing total_completed"
    assert "total_failed" in summary, "Missing total_failed"
    assert "overall_status" in summary, "Missing overall_status"
    assert "success_rate" in summary, "Missing success_rate"
    assert "most_active_queues" in summary, "Missing most_active_queues"
    
    # Verificar que los datos son consistentes
    assert summary["total_queues"] >= 0, "Invalid total_queues"
    assert summary["success_rate"] >= 0, "Invalid success_rate"
    assert summary["overall_status"] in ["healthy", "warning", "critical"], "Invalid overall_status"
    
    print(f"✅ Summary test passed: {summary['total_queues']} queues, status: {summary['overall_status']}")

def test_bullmq_visibility_summary_with_empty_queues():
    """Test para el resumen con colas vacías."""
    empty_queues = ["empty_queue_1", "empty_queue_2"]
    
    summary = bullmq_visibility_summary(empty_queues, test_redis_url)
    
    # Verificar manejo de colas vacías
    assert summary["total_waiting"] == 0, "Should have no waiting jobs"
    assert summary["total_completed"] == 0, "Should have no completed jobs"
    assert summary["total_failed"] == 0, "Should have no failed jobs"
    assert summary["success_rate"] == 0, "Should have 0% success rate"
    
    print("✅ Empty queues summary test passed")

def test_bullmq_visibility_summary_error_handling():
    """Test para manejo de errores en el resumen."""
    # Test con URL de Redis inválida
    invalid_redis_url = "redis://invalid_host:6379"
    
    summary = bullmq_visibility_summary(["test_queue"], invalid_redis_url)
    
    # Verificar que maneja errores correctamente
    assert "status" in summary or "total_queues" in summary, "Should return some status"
    
    print("✅ Summary error handling test passed")

# =============================================================================
# TESTS PARA RESUMEN POR WORKER
# =============================================================================

def test_bullmq_visibility_worker_summary_basic():
    """Test básico para resumen por worker."""
    # Setup
    worker_queues = ["worker_test_1", "worker_test_2"]
    
    # Enviar mensajes a diferentes workers
    for i, queue_name in enumerate(worker_queues):
        bullmq_sender(worker=queue_name, priority=5, message={"worker_test": i})
    
    time.sleep(1)
    
    # Test
    worker_summary = bullmq_visibility_worker_summary(worker_queues, test_redis_url)
    
    # Verificaciones
    assert "workers" in worker_summary, "Missing workers data"
    assert "total_workers" in worker_summary, "Missing total_workers"
    assert "timestamp" in worker_summary, "Missing timestamp"
    
    # Verificar estructura de workers
    for queue_name in worker_queues:
        if queue_name in worker_summary["workers"]:
            worker_data = worker_summary["workers"][queue_name]
            assert "pending_jobs" in worker_data, f"Missing pending_jobs for {queue_name}"
            assert "completed_jobs" in worker_data, f"Missing completed_jobs for {queue_name}"
            assert "failed_jobs" in worker_data, f"Missing failed_jobs for {queue_name}"
            assert "health_score" in worker_data, f"Missing health_score for {queue_name}"
            assert "status" in worker_data, f"Missing status for {queue_name}"
    
    print(f"✅ Worker summary test passed: {worker_summary['total_workers']} workers")

def test_bullmq_visibility_worker_summary_with_summary():
    """Test para verificar el resumen agregado de workers."""
    worker_queues = ["summary_worker_1", "summary_worker_2"]
    
    worker_summary = bullmq_visibility_worker_summary(worker_queues, test_redis_url)
    
    # Verificar que incluye resumen agregado cuando hay workers
    if worker_summary["workers"]:
        assert "summary" in worker_summary, "Missing aggregated summary"
        summary = worker_summary["summary"]
        assert "total_pending" in summary, "Missing total_pending in summary"
        assert "total_completed" in summary, "Missing total_completed in summary"
        assert "average_health" in summary, "Missing average_health in summary"
        assert summary["average_health"] >= 0, "Invalid average_health"
        assert summary["average_health"] <= 100, "Invalid average_health"
    
    print("✅ Worker summary with aggregated data test passed")

# =============================================================================
# TESTS PARA SALUD DE COLAS ESPECÍFICAS
# =============================================================================

def test_get_queue_health_healthy_queue():
    """Test para una cola saludable."""
    healthy_queue = "healthy_test_queue"
    
    # Enviar mensajes exitosos
    bullmq_sender(worker=healthy_queue, priority=5, message={"healthy": "test"})
    time.sleep(1)
    
    health = get_queue_health(healthy_queue, test_redis_url)
    
    # Verificaciones
    assert "queue_name" in health, "Missing queue_name"
    assert "is_healthy" in health, "Missing is_healthy flag"
    assert "total_jobs" in health, "Missing total_jobs"
    assert "pending" in health, "Missing pending count"
    assert "processing" in health, "Missing processing count"
    assert "failed" in health, "Missing failed count"
    assert "recommendations" in health, "Missing recommendations"
    
    assert health["queue_name"] == healthy_queue, "Queue name mismatch"
    assert isinstance(health["is_healthy"], bool), "is_healthy should be boolean"
    assert isinstance(health["recommendations"], list), "recommendations should be a list"
    
    print(f"✅ Queue health test passed: {healthy_queue} is_healthy={health['is_healthy']}")

def test_get_queue_health_with_recommendations():
    """Test para verificar que se generan recomendaciones apropiadas."""
    rec_queue = "recommendations_test_queue"
    
    health = get_queue_health(rec_queue, test_redis_url)
    
    # Verificar que siempre hay recomendaciones
    assert len(health["recommendations"]) > 0, "Should always have recommendations"
    
    # Verificar que las recomendaciones son strings
    for rec in health["recommendations"]:
        assert isinstance(rec, str), "Each recommendation should be a string"
    
    print(f"✅ Queue recommendations test passed: {len(health['recommendations'])} recommendations")

# =============================================================================
# TESTS PARA INFORMACIÓN EN TIEMPO REAL
# =============================================================================

def test_bullmq_visibility_realtime_basic():
    """Test básico para información en tiempo real."""
    realtime_queues = ["realtime_test_1", "realtime_test_2"]
    
    # Setup - enviar algunos mensajes
    for queue_name in realtime_queues:
        bullmq_sender(worker=queue_name, priority=5, message={"realtime": "test"})
    
    time.sleep(1)
    
    # Test
    realtime = bullmq_visibility_realtime(realtime_queues, test_redis_url)
    
    # Verificaciones
    assert "timestamp" in realtime, "Missing timestamp"
    assert "queues" in realtime, "Missing queues data"
    assert "system_status" in realtime, "Missing system_status"
    
    # Verificar system_status
    sys_status = realtime["system_status"]
    assert "total_pending" in sys_status, "Missing total_pending"
    assert "total_processing" in sys_status, "Missing total_processing"
    assert "total_failed" in sys_status, "Missing total_failed"
    assert "active_queues" in sys_status, "Missing active_queues"
    assert "healthy_queues" in sys_status, "Missing healthy_queues"
    assert "overall_health" in sys_status, "Missing overall_health"
    
    # Verificar que overall_health tiene un valor válido
    valid_health_states = ["excellent", "good", "warning", "critical"]
    assert sys_status["overall_health"] in valid_health_states, f"Invalid overall_health: {sys_status['overall_health']}"
    
    print(f"✅ Realtime test passed: {len(realtime['queues'])} queues, health: {sys_status['overall_health']}")

def test_bullmq_visibility_realtime_empty_system():
    """Test para sistema sin colas activas."""
    empty_queues = ["empty_realtime_1", "empty_realtime_2"]
    
    realtime = bullmq_visibility_realtime(empty_queues, test_redis_url)
    
    # Verificar manejo de sistema vacío
    sys_status = realtime["system_status"]
    assert sys_status["total_pending"] == 0, "Should have no pending jobs"
    assert sys_status["total_processing"] == 0, "Should have no processing jobs"
    assert sys_status["active_queues"] == 0, "Should have no active queues"
    
    print("✅ Empty system realtime test passed")

# =============================================================================
# TESTS INTEGRADOS DE FLUJO COMPLETO
# =============================================================================

def test_visibility_integration_with_processing():
    """Test de integración que verifica visibilidad mientras se procesan mensajes."""
    integration_queue = "integration_test_queue"
    visibility_test_worker.processed_messages = []
    
    def run_consumer():
        """Consumer para el test de integración."""
        try:
            consumer_bullmq(worker=integration_queue, function_process=visibility_test_worker)
        except KeyboardInterrupt:
            pass
    
    # Iniciar consumer
    consumer_thread = threading.Thread(target=run_consumer)
    consumer_thread.daemon = True
    consumer_thread.start()
    
    # Enviar mensajes
    job_ids = []
    for i in range(3):
        job_id = bullmq_sender(
            worker=integration_queue,
            priority=5,
            message={"integration_test": i, "message": f"Test message {i}"}
        )
        job_ids.append(job_id)
    
    # Esperar procesamiento
    time.sleep(3)
    
    # Verificar visibilidad durante procesamiento
    health = get_queue_health(integration_queue, test_redis_url)
    summary = bullmq_visibility_summary([integration_queue], test_redis_url)
    realtime = bullmq_visibility_realtime([integration_queue], test_redis_url)
    
    # Verificaciones
    assert health["queue_name"] == integration_queue, "Health queue name mismatch"
    assert summary["total_queues"] >= 1, "Summary should show at least 1 queue"
    assert integration_queue in realtime["queues"], "Realtime should include integration queue"
    
    # Verificar que se procesaron algunos mensajes
    if len(visibility_test_worker.processed_messages) > 0:
        processed_msg = visibility_test_worker.processed_messages[0]
        assert "integration_test" in processed_msg, "Processed message should contain test data"
    
    print(f"✅ Integration test passed: {len(job_ids)} messages sent, {len(visibility_test_worker.processed_messages)} processed")

def test_visibility_performance_with_multiple_queues():
    """Test de rendimiento con múltiples colas."""
    perf_queues = [f"perf_queue_{i}" for i in range(5)]
    
    start_time = time.time()
    
    # Test todas las funciones de visibilidad
    summary = bullmq_visibility_summary(perf_queues, test_redis_url)
    worker_summary = bullmq_visibility_worker_summary(perf_queues, test_redis_url)
    realtime = bullmq_visibility_realtime(perf_queues, test_redis_url)
    
    end_time = time.time()
    total_time = end_time - start_time
    
    # Verificar que se completó en tiempo razonable (menos de 10 segundos)
    assert total_time < 10, f"Visibility functions took too long: {total_time:.2f}s"
    
    # Verificar que todas las funciones retornaron datos válidos
    assert "total_queues" in summary, "Summary missing total_queues"
    assert "workers" in worker_summary, "Worker summary missing workers"
    assert "system_status" in realtime, "Realtime missing system_status"
    
    print(f"✅ Performance test passed: {len(perf_queues)} queues processed in {total_time:.2f}s")

# =============================================================================
# FUNCIÓN DE CLEANUP Y UTILIDADES
# =============================================================================

def test_cleanup_visibility_queues():
    """Función para limpiar colas de test de visibilidad."""
    from bullmq import Queue
    
    async def cleanup_queues():
        all_test_queues = [
            "visibility_test_1", "visibility_test_2", "visibility_test_3",
            "visibility_stats_test", "worker_test_1", "worker_test_2",
            "healthy_test_queue", "recommendations_test_queue",
            "realtime_test_1", "realtime_test_2", "integration_test_queue"
        ] + [f"perf_queue_{i}" for i in range(5)]
        
        for queue_name in all_test_queues:
            try:
                queue = Queue(queue_name, {"connection": test_redis_url})
                await queue.clean(0, 1000, "completed")
                await queue.clean(0, 1000, "failed")
                await queue.close()
            except Exception as e:
                print(f"Warning: Could not clean queue {queue_name}: {e}")
    
    asyncio.run(cleanup_queues())
    print("✅ Visibility test queues cleaned")

# Función principal para ejecutar todos los tests
if __name__ == "__main__":
    print("=== BullMQ Visibility Tests ===")
    
    try:
        # Tests básicos de estadísticas
        test_get_queue_stats_with_existing_queue()
        test_get_queue_stats_with_nonexistent_queue()
        
        # Tests de resumen general
        test_bullmq_visibility_summary_with_data()
        test_bullmq_visibility_summary_with_empty_queues()
        test_bullmq_visibility_summary_error_handling()
        
        # Tests de resumen por worker
        test_bullmq_visibility_worker_summary_basic()
        test_bullmq_visibility_worker_summary_with_summary()
        
        # Tests de salud de colas
        test_get_queue_health_healthy_queue()
        test_get_queue_health_with_recommendations()
        
        # Tests de tiempo real
        test_bullmq_visibility_realtime_basic()
        test_bullmq_visibility_realtime_empty_system()
        
        # Tests de integración
        test_visibility_integration_with_processing()
        test_visibility_performance_with_multiple_queues()
        
        # Cleanup
        test_cleanup_visibility_queues()
        
        print("✅ All visibility tests passed!")
        
    except Exception as e:
        print(f"❌ Test failed: {e}")
        raise
