"""TaskProcessor - Handles individual task execution with circuit breaker and retry logic."""

import logging
import time
import threading
from queue import Empty
from typing import Callable, Optional, Tuple, Any


class TaskProcessor:
    """Processes individual tasks from queue with fault tolerance and retry."""
    
    def __init__(
        self,
        circuit_breaker,
        retry_strategy,
        metrics_collector,
        auditor,
        executor=None,
        max_retries: int = 3,
    ):
        """Initialize task processor.
        
        Args:
            circuit_breaker: CircuitBreaker instance for fault tolerance
            retry_strategy: RetryStrategy instance for retry decisions
            metrics_collector: Metrics collector for performance tracking
            auditor: Auditor for task completion logging
            executor: ThreadPoolExecutor for async requeue (optional)
            max_retries: Maximum retry attempts
        """
        self.circuit_breaker = circuit_breaker
        self.retry_strategy = retry_strategy
        self.metrics_collector = metrics_collector
        self.auditor = auditor
        self.executor = executor
        self.max_retries = max_retries
    
    def check_ttl(
        self,
        t_arrival: float,
        ttl: float,
        m_id: str,
        corr_id: Optional[str] = None,
    ) -> bool:
        """Check if task has expired by TTL.
        
        Args:
            t_arrival: Task arrival timestamp
            ttl: Time to live in seconds
            m_id: Message ID
            corr_id: Correlation ID for tracing
            
        Returns:
            True if task is still valid, False if expired.
        """
        if (time.time() - t_arrival) > ttl:
            if corr_id:
                logging.debug(f"[TRACE:{corr_id}] Task expired by TTL")
            return False
        return True
    
    def execute_task(
        self,
        m_id: str,
        task: str,
        work_fn: Callable,
        corr_id: Optional[str] = None,
    ) -> Tuple[bool, Any]:
        """Execute task with circuit breaker protection.
        
        Args:
            m_id: Message ID
            task: Task description
            work_fn: Callable that performs the work
            corr_id: Correlation ID for tracing
            
        Returns:
            Tuple of (success: bool, result: Any)
        """
        if corr_id:
            logging.debug(f"[TRACE:{corr_id}] Starting processing of {m_id}")
        
        try:
            result = self.circuit_breaker.call(work_fn)
            return True, result
        except Exception as e:
            if corr_id:
                logging.error(f"[TRACE:{corr_id}] Task execution failed: {str(e)}")
            return False, e
    
    def should_retry(self, error: Exception, attempts: int) -> bool:
        """Determine if task should be retried.
        
        Args:
            error: Exception that occurred
            attempts: Number of attempts made so far
            
        Returns:
            True if should retry, False if should fail permanently.
        """
        return self.retry_strategy.should_retry(error, attempts, self.max_retries)
    
    def get_retry_delay(self, attempts: int) -> float:
        """Get delay before next retry.
        
        Args:
            attempts: Number of attempts made so far
            
        Returns:
            Delay in seconds before retry.
        """
        return self.retry_strategy.get_delay(attempts, self.max_retries)
    
    def requeue_task(
        self,
        inbox,
        priority: int,
        t_arrival: float,
        ttl: float,
        complexity: int,
        attempts: int,
        m_id: str,
        task: str,
        proto: str,
        corr_id: str,
        delay: float,
    ) -> None:
        """Requeue task after delay with incremented attempt counter.
        
        Args:
            inbox: PriorityQueue to requeue task into
            priority: Task priority
            t_arrival: Original arrival timestamp
            ttl: Time to live
            complexity: Task complexity
            attempts: Current attempt number
            m_id: Message ID
            task: Task description
            proto: Protocol
            corr_id: Correlation ID
            delay: Delay before requeue in seconds
        """
        def _requeue_delayed():
            time.sleep(delay)
            inbox.put(
                (priority, t_arrival, ttl, complexity, attempts + 1, m_id, task, proto, corr_id)
            )
        
        if self.executor:
            self.executor.submit(_requeue_delayed)
        else:
            threading.Thread(target=_requeue_delayed, daemon=True).start()
    
    def record_success(
        self,
        m_id: str,
        t_start: float,
        corr_id: Optional[str] = None,
    ) -> float:
        """Record successful task completion.
        
        Args:
            m_id: Message ID
            t_start: Task start timestamp
            corr_id: Correlation ID for tracing
            
        Returns:
            Latency in seconds.
        """
        latency = time.time() - t_start
        self.metrics_collector.registrar_fin_tarea(m_id, exitosa=True)
        if self.auditor:
            self.auditor.registrar_tarea_completada(m_id, latency)
        
        if corr_id:
            logging.debug(f"[TRACE:{corr_id}] Task {m_id} completed successfully")
        
        return latency
    
    def record_failure(
        self,
        m_id: str,
        error: Exception,
        corr_id: Optional[str] = None,
    ) -> None:
        """Record failed task completion.
        
        Args:
            m_id: Message ID
            error: Exception that occurred
            corr_id: Correlation ID for tracing
        """
        self.metrics_collector.registrar_fin_tarea(m_id, exitosa=False)
        if self.auditor:
            self.auditor.registrar_error(m_id, type(error).__name__, str(error)[:100])
        
        if corr_id:
            logging.error(f"[TRACE:{corr_id}] Final failure in {m_id}: {str(error)}")
    
    def record_ttl_expiration(self, m_id: str) -> None:
        """Record task expiration by TTL."""
        self.metrics_collector.registrar_fin_tarea(m_id, exitosa=False)
        logging.warning(f"Task {m_id} expired by TTL")
