"""
Dead Letter Queue

Manages failed intents with retry mechanism.
"""

import threading
import time
from typing import Dict, List, Optional
from dataclasses import dataclass
from enum import Enum

from altrus.core.intent.intent import Intent


class FailureReason(Enum):
    """Reasons for intent failure"""
    TIMEOUT = "TIMEOUT"
    MODULE_UNAVAILABLE = "MODULE_UNAVAILABLE"
    EXECUTION_ERROR = "EXECUTION_ERROR"
    VALIDATION_ERROR = "VALIDATION_ERROR"
    REJECTED = "REJECTED"
    UNKNOWN = "UNKNOWN"


@dataclass
class DeadLetterEntry:
    """Entry in the dead letter queue"""
    intent: Intent
    failure_reason: FailureReason
    failure_time: float
    retry_count: int = 0
    last_retry_time: Optional[float] = None
    metadata: Optional[Dict] = None


class DeadLetterQueue:
    """
    Dead letter queue for failed intents with exponential backoff retry.

    Features:
    - Automatic retry with exponential backoff
    - Max retry limit
    - Failure reason tracking
    - Inspection API
    """

    def __init__(
        self,
        max_retries: int = 3,
        initial_backoff: float = 1.0,
        max_backoff: float = 60.0,
        backoff_multiplier: float = 2.0
    ):
        self._queue: Dict[str, DeadLetterEntry] = {}
        self._lock = threading.Lock()

        # Configuration
        self._max_retries = max_retries
        self._initial_backoff = initial_backoff
        self._max_backoff = max_backoff
        self._backoff_multiplier = backoff_multiplier

        # Metrics
        self._total_failed = 0
        self._total_retried = 0
        self._total_exhausted = 0  # Exceeded max retries

    def add(
        self,
        intent: Intent,
        reason: FailureReason,
        metadata: Optional[Dict] = None
    ):
        """Add a failed intent to the dead letter queue"""
        with self._lock:
            entry = DeadLetterEntry(
                intent=intent,
                failure_reason=reason,
                failure_time=time.time(),
                metadata=metadata or {}
            )
            self._queue[intent.intent_id] = entry
            self._total_failed += 1

    def get_retry_candidates(self) -> List[Intent]:
        """
        Get intents that are ready for retry based on backoff time.

        Returns:
            List of intents ready to retry
        """
        candidates = []
        current_time = time.time()

        with self._lock:
            for entry in self._queue.values():
                # Check if max retries exceeded
                if entry.retry_count >= self._max_retries:
                    continue

                # Calculate backoff time
                backoff = min(
                    self._initial_backoff * (self._backoff_multiplier ** entry.retry_count),
                    self._max_backoff
                )

                # Check if enough time has passed
                last_attempt = entry.last_retry_time or entry.failure_time
                if current_time - last_attempt >= backoff:
                    candidates.append(entry.intent)

        return candidates

    def mark_retried(self, intent_id: str):
        """Mark an intent as retried (increment retry count)"""
        with self._lock:
            if intent_id in self._queue:
                self._queue[intent_id].retry_count += 1
                self._queue[intent_id].last_retry_time = time.time()
                self._total_retried += 1

    def remove(self, intent_id: str) -> bool:
        """
        Remove an intent from the queue (successfully recovered).

        Returns:
            True if removed, False if not found
        """
        with self._lock:
            if intent_id in self._queue:
                del self._queue[intent_id]
                return True
            return False

    def get_exhausted(self) -> List[DeadLetterEntry]:
        """Get intents that have exceeded max retry limit"""
        with self._lock:
            exhausted = [
                entry for entry in self._queue.values()
                if entry.retry_count >= self._max_retries
            ]
            return exhausted.copy()

    def purge_exhausted(self) -> int:
        """
        Remove all intents that have exceeded max retries.

        Returns:
            Number of intents removed
        """
        with self._lock:
            exhausted_ids = [
                intent_id for intent_id, entry in self._queue.items()
                if entry.retry_count >= self._max_retries
            ]

            for intent_id in exhausted_ids:
                del self._queue[intent_id]
                self._total_exhausted += 1

            return len(exhausted_ids)

    def get_all(self) -> List[DeadLetterEntry]:
        """Get all entries in the dead letter queue"""
        with self._lock:
            return list(self._queue.values())

    def get_by_reason(self, reason: FailureReason) -> List[DeadLetterEntry]:
        """Get entries filtered by failure reason"""
        with self._lock:
            return [
                entry for entry in self._queue.values()
                if entry.failure_reason == reason
            ]

    def get_metrics(self) -> dict:
        """Get dead letter queue metrics"""
        with self._lock:
            reason_counts = {}
            for entry in self._queue.values():
                reason = entry.failure_reason.value
                reason_counts[reason] = reason_counts.get(reason, 0) + 1

            return {
                "queue_size": len(self._queue),
                "total_failed": self._total_failed,
                "total_retried": self._total_retried,
                "total_exhausted": self._total_exhausted,
                "failure_reasons": reason_counts,
                "max_retries": self._max_retries
            }

    def clear(self):
        """Clear all entries (for testing/shutdown)"""
        with self._lock:
            self._queue.clear()
