"""Queue management policies."""

import heapq
import logging
import threading
from collections import deque
from enum import Enum
from typing import Optional


class PoliticaCola(Enum):
    """Políticas de despacho de la cola."""
    FIFO = "fifo"  # First In First Out (orden de llegada)
    LIFO = "lifo"  # Last In First Out (orden inverso)
    PRIORITY = "priority"  # Por prioridad y complejidad


class GestorColaPolitica:
    """Gestiona despacho de tareas según política configurada."""
    
    def __init__(self, politica: PoliticaCola = PoliticaCola.PRIORITY):
        """
        Inicializa gestor de cola.
        
        Args:
            politica: Política de despacho (FIFO, LIFO, PRIORITY)
        """
        self.politica = politica
        # Mantenemos colas internas para cada política
        self.cola_fifo: deque = deque()
        self.cola_prioridad: list = []  # Heap: (prioridad, t_llegada, tarea_info)
        self.lock = threading.Lock()

    def aplicar_politica(self, prioridad: int, t_llegada: float) -> tuple[int, float]:
        """Calcula prioridad efectiva y timestamp según política.

        Returns:
            (prioridad_efectiva, t_llegada_efectiva)
        """
        if self.politica == PoliticaCola.FIFO:
            return 0, t_llegada
        if self.politica == PoliticaCola.LIFO:
            return 0, -t_llegada
        return prioridad, t_llegada
    
    def encolar(self, tarea_info: tuple):
        """Encola una tarea según la política."""
        with self.lock:
            if self.politica == PoliticaCola.FIFO:
                self.cola_fifo.append(tarea_info)
            elif self.politica == PoliticaCola.LIFO:
                self.cola_fifo.append(tarea_info)  # Se usa como stack
            elif self.politica == PoliticaCola.PRIORITY:
                # tarea_info = (prioridad, t_llegada, ttl, comp, intentos, m_id, tarea, proto, corr_id)
                heapq.heappush(self.cola_prioridad, (tarea_info[0], tarea_info[1], tarea_info))
    
    def desencolar(self) -> Optional[tuple]:
        """Desencola una tarea según la política."""
        with self.lock:
            if self.politica == PoliticaCola.FIFO:
                return self.cola_fifo.popleft() if self.cola_fifo else None
            elif self.politica == PoliticaCola.LIFO:
                return self.cola_fifo.pop() if self.cola_fifo else None
            elif self.politica == PoliticaCola.PRIORITY:
                if self.cola_prioridad:
                    _, _, tarea_info = heapq.heappop(self.cola_prioridad)
                    return tarea_info
                return None
        return None
    
    def qsize(self) -> int:
        """Retorna tamaño de la cola."""
        with self.lock:
            if self.politica in [PoliticaCola.FIFO, PoliticaCola.LIFO]:
                return len(self.cola_fifo)
            else:
                return len(self.cola_prioridad)
    
    def cambiar_politica(self, nueva_politica: PoliticaCola):
        """Cambia la política de despacho."""
        with self.lock:
            # Si hay tareas encoladas con la política anterior, 
            # conservarlas (es un comportamiento especial)
            logging.info(f"Cambiando política de cola a {nueva_politica.value}")
            self.politica = nueva_politica
    
    def limpiar(self):
        """Limpia la cola."""
        with self.lock:
            self.cola_fifo.clear()
            self.cola_prioridad.clear()
