import pytest
import time
from datetime import datetime, timezone
from tredops_eval.connect import db
from .bullmaq_backup import (
    bullmq_backup,
    bullmq_restore,
    list_backups,
    bullmq_delete_backup,
    mongodb_collection_backup
)
from .bullmq_sender import bullmq_sender
import threading

def backup_test_worker(message):
    """Worker de prueba para tests de backup."""
    print(f"Backup test processing: {message}")
    time.sleep(0.2)
    return {"status": "processed", "data": message}

@pytest.mark.skipif(db is None, reason="Requires real MongoDB connection")
def test_bullmq_backup_specialized():
    """Test especializado en crear backup verificando datos de BullMQ correctamente."""
    redis_url = "redis://192.168.1.163:6379"
    backup_name = "specialized_backup_test"
    test_queue = "backup_specialized_queue"
    
    # Limpiar backup anterior si existe
    cleanup_result = bullmq_delete_backup(backup_name, db=db)
    print(f"Cleanup before test: {cleanup_result.get('message', 'No previous backup')}")
    
    # Crear jobs específicos para el test de backup
    test_jobs = [
        {
            "priority": 5,
            "delay_sec": 0,
            "message": {"test_type": "immediate", "data": "Job inmediato"},
            "job_id": "immediate_job_1"
        },
        {
            "priority": 8, 
            "delay_sec": 3,
            "message": {"test_type": "delayed", "data": "Job con delay"},
            "job_id": "delayed_job_1"
        },
        {
            "priority": 3,
            "delay_sec": 0,
            "message": {"test_type": "low_priority", "data": "Job baja prioridad"},
            "job_id": "low_priority_job_1"
        }
    ]
    
    created_job_ids = []
    for job_config in test_jobs:
        job_id = bullmq_sender(
            worker=test_queue,
            priority=job_config["priority"],
            delay_sec=job_config["delay_sec"],
            message=job_config["message"],
            redis_url=redis_url,
            job_id=job_config["job_id"]
        )
        created_job_ids.append(job_id)
        print(f"Created job: {job_id} with priority {job_config['priority']}")
    
    # Esperar que se registren los jobs en BullMQ
    time.sleep(2)
    
    # Realizar backup
    print(f"\nCreating backup '{backup_name}'...")
    backup_result = bullmq_backup(
        db=db,
        queue_names=[test_queue],
        redis_url=redis_url,
        backup_name=backup_name
    )
    
    # Verificaciones del resultado del backup
    assert backup_result["status"] == "success", f"Backup failed: {backup_result}"
    assert backup_result["backup_id"] == backup_name, f"Backup ID mismatch: expected {backup_name}, got {backup_result['backup_id']}"
    assert backup_result["total_jobs"] >= 0, "Total jobs should be non-negative"
    assert backup_result["total_queues"] >= 0, "Total queues should be non-negative"
    
    print(f"Backup completed: {backup_result['total_jobs']} jobs from {backup_result['total_queues']} queues")
    
    # Verificar que los datos se guardaron correctamente en MongoDB
    collection = db[mongodb_collection_backup]
    backup_documents = list(collection.find({"backup_id": backup_name}))
    
    print(f"Found {len(backup_documents)} backup documents in MongoDB")
    
    # Verificar estructura de los documentos de backup
    if len(backup_documents) > 0:
        sample_doc = backup_documents[0]
        
        # Verificar campos requeridos en cada documento
        required_fields = ["backup_id", "createAt", "worker", "message", "priority"]
        for field in required_fields:
            assert field in sample_doc, f"Missing required field '{field}' in backup document"
        
        # Verificar que el backup_id coincide
        for doc in backup_documents:
            assert doc["backup_id"] == backup_name, f"Document backup_id mismatch: {doc['backup_id']}"
            assert doc["worker"] == test_queue, f"Document worker mismatch: {doc['worker']}"
            assert isinstance(doc["message"], dict), "Message should be a dictionary"
            assert isinstance(doc["priority"], int), "Priority should be an integer"
        
        print("✅ Backup document structure verified correctly")
        
        # Verificar contenido de los mensajes
        found_messages = [doc["message"] for doc in backup_documents]
        test_types_found = [msg.get("test_type") for msg in found_messages if "test_type" in msg]
        
        print(f"Found test types in backup: {test_types_found}")
        
    else:
        print("ℹ️  No backup documents found (jobs may have been processed quickly)")
    
    # Limpiar después del test
    cleanup_result = bullmq_delete_backup(backup_name, db=db)
    print(f"Cleanup after test: {cleanup_result['message']}")

@pytest.mark.skipif(db is None, reason="Requires real MongoDB connection")
def test_bullmq_restore_specialized():
    """Test especializado en restore verificando que los datos se restauran correctamente."""
    redis_url = "redis://192.168.1.163:6379"
    backup_name = "specialized_restore_test"
    test_queue = "restore_specialized_queue"
    
    # Limpiar backup anterior si existe
    bullmq_delete_backup(backup_name, db=db)
    
    # Fase 1: Crear jobs y backup para restaurar
    print("Phase 1: Creating jobs for backup...")
    test_jobs_data = [
        {
            "priority": 7,
            "delay_sec": 2,
            "message": {"restore_test": True, "job_type": "high_priority", "content": "Important job"}
        },
        {
            "priority": 4,
            "delay_sec": 0,
            "message": {"restore_test": True, "job_type": "normal", "content": "Regular job"}
        }
    ]
    
    original_job_ids = []
    for i, job_data in enumerate(test_jobs_data):
        job_id = bullmq_sender(
            worker=test_queue,
            priority=job_data["priority"],
            delay_sec=job_data["delay_sec"],
            message=job_data["message"],
            redis_url=redis_url
        )
        original_job_ids.append(job_id)
        print(f"Created original job: {job_id}")
    
    # Esperar registros
    time.sleep(2)
    
    # Crear backup
    backup_result = bullmq_backup(
        db=db,
        queue_names=[test_queue],
        redis_url=redis_url,
        backup_name=backup_name
    )
    
    assert backup_result["status"] == "success", f"Initial backup failed: {backup_result}"
    print(f"Backup created with {backup_result['total_jobs']} jobs")
    
    # Verificar que existe el backup en la base de datos
    collection = db[mongodb_collection_backup]
    backup_docs_before_restore = list(collection.find({"backup_id": backup_name}))
    assert len(backup_docs_before_restore) >= 0, "Should have backup documents"
    
    print(f"Backup contains {len(backup_docs_before_restore)} job documents")
    
    # Fase 2: Restaurar el backup
    print("\nPhase 2: Restoring backup...")
    restore_result = bullmq_restore(
        db=db,
        backup_id=backup_name,
        redis_url=redis_url
    )
    
    # Verificaciones del restore
    assert restore_result["backup_id"] == backup_name, f"Restore backup_id mismatch: expected {backup_name}, got {restore_result['backup_id']}"
    assert "total_jobs_restored" in restore_result, "Missing total_jobs_restored in result"
    assert "queues_restored" in restore_result, "Missing queues_restored in result"
    assert "queue_details" in restore_result, "Missing queue_details in result"
    
    print(f"Restore completed: {restore_result['total_jobs_restored']} jobs restored to {restore_result['queues_restored']} queues")
    
    # Verificar detalles de la cola restaurada
    if restore_result["total_jobs_restored"] > 0:
        assert test_queue in restore_result["queue_details"], f"Queue {test_queue} not in restore details"
        queue_detail = restore_result["queue_details"][test_queue]
        
        assert "jobs_restored" in queue_detail, "Missing jobs_restored in queue detail"
        assert "status" in queue_detail, "Missing status in queue detail"
        assert queue_detail["status"] in ["success", "no_jobs"], f"Invalid queue status: {queue_detail['status']}"
        
        print(f"Queue '{test_queue}' restore details: {queue_detail}")
    
    # Verificar que los jobs restaurados contienen la metadata correcta
    if len(backup_docs_before_restore) > 0:
        print("Verifying restored job metadata...")
        
        # Los jobs restaurados deberían tener metadata adicional
        restored_jobs_sample = backup_docs_before_restore[0]
        original_message = restored_jobs_sample.get("message", {})
        
        # Verificar que los datos originales están presentes
        if "restore_test" in original_message:
            assert original_message["restore_test"] == True, "Original restore_test flag should be preserved"
        
        if "job_type" in original_message:
            assert original_message["job_type"] in ["high_priority", "normal"], "Original job_type should be preserved"
        
        print("✅ Job metadata verification completed")
    
    # Fase 3: Verificar que el restore funciona sin especificar backup_id (debería usar el más reciente)
    print("\nPhase 3: Testing restore with latest backup...")
    latest_restore_result = bullmq_restore(db=db, redis_url=redis_url)
    
    # Debería usar el mismo backup que acabamos de crear
    assert latest_restore_result["backup_id"] == backup_name, f"Latest restore should use backup '{backup_name}'"
    
    print(f"Latest restore used backup: {latest_restore_result['backup_id']}")
    
    # Limpiar después del test
    cleanup_result = bullmq_delete_backup(backup_name, db=db)
    print(f"Cleanup: {cleanup_result['message']}")

@pytest.mark.skipif(db is None, reason="Requires real MongoDB connection") 
def test_list_and_delete_backups():
    """Test para verificar listado y eliminación de backups con base de datos real."""
    test_backup_name = "list_delete_test_backup"
    test_queue = "list_delete_test_queue"
    
    # Limpiar backup anterior
    bullmq_delete_backup(test_backup_name, db=db)
    
    # Crear un job y backup para probar
    job_id = bullmq_sender(
        worker=test_queue,
        priority=5,
        message={"list_delete_test": True, "content": "Test data"}
    )
    time.sleep(1)
    
    backup_result = bullmq_backup(
        db=db,
        queue_names=[test_queue],
        backup_name=test_backup_name
    )
    
    assert backup_result["status"] == "success", "Failed to create test backup"
    
    # Probar listado de backups
    backups_list = list_backups(db)
    
    assert isinstance(backups_list, list), "list_backups should return a list"
    
    # Buscar nuestro backup en la lista
    found_backup = None
    for backup in backups_list:
        if backup["backup_id"] == test_backup_name:
            found_backup = backup
            break
    
    assert found_backup is not None, f"Backup '{test_backup_name}' should be in the list"
    assert "total_jobs" in found_backup, "Backup should have total_jobs field"
    assert "total_queues" in found_backup, "Backup should have total_queues field"
    
    print(f"Found backup in list: {found_backup}")
    
    # Probar eliminación
    delete_result = bullmq_delete_backup(test_backup_name, db=db)
    
    assert delete_result["status"] == "success", f"Failed to delete backup: {delete_result}"
    assert delete_result["backup_id"] == test_backup_name, "Delete result should have correct backup_id"
    assert delete_result["deleted_count"] > 0, "Should have deleted at least one document"
    
    print(f"Delete result: {delete_result['message']}")
    
    # Verificar que el backup ya no existe
    collection = db[mongodb_collection_backup]
    remaining_docs = list(collection.find({"backup_id": test_backup_name}))
    assert len(remaining_docs) == 0, "Backup documents should be completely removed"
    
    print("✅ List and delete operations verified successfully")

def test_error_handling():
    """Test de manejo de errores básico."""
    # Test backup sin base de datos
    backup_result = bullmq_backup(db=None, backup_name="error_test")
    assert backup_result["status"] == "error"
    assert "Database connection required" in backup_result["message"]
    
    # Test restore sin base de datos
    restore_result = bullmq_restore(db=None)
    assert restore_result["status"] == "error" 
    assert "Database connection required" in restore_result["message"]
    
    if db is not None:
        # Test restore con backup inexistente
        restore_result = bullmq_restore(db=db, backup_id="nonexistent_backup_xyz")
        assert restore_result["status"] == "error"
        assert "not found" in restore_result["message"]
    
    print("✅ Error handling tests passed")