"""QueueManager - Centralized queue validation, backpressure, and task cancellation."""

import heapq
import logging
import time
import uuid
from typing import Optional, Tuple, Callable


class QueueManager:
    """Manages task queue with validation, backpressure, and cancellation."""
    
    def __init__(
        self,
        inbox,
        capacity_getter: Callable[[], int],
        backpressure_threshold: float = 0.8
    ):
        """Initialize queue manager.
        
        Args:
            inbox: PriorityQueue instance for tasks
            capacity_getter: Callable that returns current capacity
            backpressure_threshold: Threshold for backpressure (0.0-1.0)
        """
        self.inbox = inbox
        self.capacity_getter = capacity_getter
        self.backpressure_threshold = backpressure_threshold
    
    def validate_capacity(self) -> bool:
        """Check if queue is at capacity.
        
        Returns:
            True if queue can accept tasks, False if full.
        """
        capacity = self.capacity_getter()
        
        if capacity <= 0:
            logging.warning("Invalid capacity: must be > 0")
            return False
        
        if self.inbox.qsize() >= capacity:
            return False
        
        return True
    
    def apply_backpressure(self, max_delay: float = 0.5) -> float:
        """Apply backpressure delay based on queue load.
        
        Args:
            max_delay: Maximum delay in seconds
            
        Returns:
            Actual delay applied (0 if no backpressure needed)
        """
        capacity = self.capacity_getter()
        if capacity <= 0:
            return 0.0
            
        current_load = self.inbox.qsize() / capacity
        
        if current_load < self.backpressure_threshold:
            return 0.0
        
        # Calculate excess load beyond threshold
        excess = min(
            1.0, 
            (current_load - self.backpressure_threshold) / max(0.01, (1 - self.backpressure_threshold))
        )
        
        delay = max_delay * excess
        return delay
    
    def enqueue_task(
        self,
        priority: int,
        ttl: float,
        complexity: int,
        m_id: str,
        task: str,
        protocol: str,
        correlation_id: str,
        t_arrival_override: Optional[float] = None,
    ) -> None:
        """Add task to queue.
        
        Args:
            priority: Task priority (lower = higher priority)
            ttl: Time to live in seconds
            complexity: Task complexity (1-10)
            m_id: Message ID (unique identifier)
            task: Task description
            protocol: Protocol used (FIPA, KQML, JSON-RPC, SIMPLE)
            correlation_id: Correlation ID for distributed tracing
            t_arrival_override: Optional timestamp override for queue policies
        """
        t_arrival = t_arrival_override if t_arrival_override is not None else time.time()
        self.inbox.put(
            (priority, t_arrival, ttl, complexity, 0, m_id, task, protocol, correlation_id)
        )
    
    def cancel_task(self, m_id: str) -> bool:
        """Cancel a queued task by its ID.
        
        Args:
            m_id: Message ID to cancel
            
        Returns:
            True if task was removed, False if not found.
        """
        if not m_id:
            return False
        
        removed = 0
        with self.inbox.mutex:
            items = list(self.inbox.queue)
            self.inbox.queue.clear()
            
            for item in items:
                # Check if this is the task to cancel
                if item[5] == m_id:
                    removed += 1
                    continue
                # Keep all other tasks
                self.inbox.queue.append(item)
            
            heapq.heapify(self.inbox.queue)
            
            if removed:
                self.inbox.unfinished_tasks = max(
                    0, self.inbox.unfinished_tasks - removed
                )
        
        return removed > 0
    
    def get_queue_size(self) -> int:
        """Get current queue size."""
        return self.inbox.qsize()
    
    def get_queue_load(self) -> float:
        """Get queue load as percentage (0.0-1.0)."""
        capacity = self.capacity_getter()
        if capacity <= 0:
            return 0.0
        return self.inbox.qsize() / capacity
    
    def clear_queue(self) -> int:
        """Clear all tasks from queue.
        
        Returns:
            Number of tasks removed.
        """
        removed = 0
        with self.inbox.mutex:
            removed = len(self.inbox.queue)
            self.inbox.queue.clear()
            self.inbox.unfinished_tasks = 0
        return removed

