"""Lifecycle hooks and schemas."""

import json
import logging
import threading
from typing import Any, Callable, Dict, Optional


class EventoSchema:
    """Schema para eventos versionados con validación."""
    
    SCHEMA_VERSION = "1.0"
    
    TIPOS_VALIDOS = {
        "EXITO", "ERROR", "TIMEOUT", "REINTENTO", "INCIDENTE",
        "TTL_EXPIRADO", "SATURACION", "CUSTOM"
    }
    
    @staticmethod
    def validar(tipo: str, datos: Dict[str, Any], max_kb: int = 100) -> bool:
        """Valida evento contra schema."""
        if tipo not in EventoSchema.TIPOS_VALIDOS:
            raise ValueError(f"Tipo inválido: {tipo}")
        
        size_bytes = len(json.dumps(datos, default=str).encode('utf-8'))
        if size_bytes > max_kb * 1024:
            raise ValueError(f"Evento > {max_kb}KB")
        
        return True


class LifecycleHooks:
    """Gestiona hooks del ciclo de vida del agente."""
    
    def __init__(self):
        self._hooks = {
            "on_start": [],
            "on_stop": [],
            "on_error": [],
        }
        self.lock = threading.Lock()
    
    def registrar(self, evento: str, callback: Callable):
        """Registra callback para evento del ciclo de vida."""
        if evento not in self._hooks:
            raise ValueError(f"Evento inválido: {evento}")
        with self.lock:
            self._hooks[evento].append(callback)
    
    def invocar(self, evento: str, *args, **kwargs):
        """Invoca callbacks registrados para evento."""
        with self.lock:
            callbacks = self._hooks.get(evento, []).copy()
        for cb in callbacks:
            try:
                if callable(cb):
                    cb(*args, **kwargs)
            except Exception as e:
                logging.error(f"Error en hook {evento}: {e}")


class GestorAntiInanicion:
    """Previene inanición (starvation) de tareas de baja prioridad.
    
    Las tareas de baja prioridad pueden quedar bloqueadas indefinidamente
    si el sistema recibe continuamente tareas de alta prioridad. Este gestor
    asegura que cada N tareas de alta prioridad, se procese al menos una
    de baja prioridad.
    """
    
    def __init__(self, ratio_minimo_baja_prioridad: float = 0.2):
        """
        Inicializa gestor anti-inanición.
        
        Args:
            ratio_minimo_baja_prioridad: Proporción mínima de tareas de baja 
                                        prioridad a ejecutar (0.0 a 1.0)
        """
        self.ratio_minimo = max(0.0, min(1.0, ratio_minimo_baja_prioridad))
        self.contador_tareas = 0
        self.tareas_baja_prioridad_ejecutadas = 0
        self.lock = threading.Lock()
    
    def puede_saltarse_baja_prioridad(self) -> bool:
        """Determina si se puede saltarse una tarea de baja prioridad."""
        with self.lock:
            if self.contador_tareas == 0:
                return False
            
            # Si no hemos alcanzado el ratio mínimo, NO saltamos tareas de baja prioridad
            ratio_actual = self.tareas_baja_prioridad_ejecutadas / self.contador_tareas
            puede_saltarse = ratio_actual >= self.ratio_minimo
            
            return puede_saltarse
    
    def registrar_tarea_ejecutada(self, es_baja_prioridad: bool):
        """Registra una tarea ejecutada."""
        with self.lock:
            self.contador_tareas += 1
            if es_baja_prioridad:
                self.tareas_baja_prioridad_ejecutadas += 1
    
    def obtener_estadisticas(self) -> Dict[str, Any]:
        """Obtiene estadísticas del gestor anti-inanición."""
        with self.lock:
            if self.contador_tareas == 0:
                ratio = 0.0
            else:
                ratio = self.tareas_baja_prioridad_ejecutadas / self.contador_tareas
            
            return {
                "total_tareas": self.contador_tareas,
                "tareas_baja_prioridad": self.tareas_baja_prioridad_ejecutadas,
                "ratio_actual": ratio,
                "ratio_minimo_requerido": self.ratio_minimo,
            }
    
    def limpiar(self):
        """Limpia estadísticas."""
        with self.lock:
            self.contador_tareas = 0
            self.tareas_baja_prioridad_ejecutadas = 0
