"""
Intent Timeout Manager

Tracks and enforces timeouts for executing intents.
"""

import threading
import time
from typing import Dict, Callable, Optional
from dataclasses import dataclass

from altrus.core.intent.intent import Intent


@dataclass
class TimeoutConfig:
    """Timeout configuration for a capability"""
    default_timeout: float = 30.0  # seconds
    capability_timeouts: Dict[str, float] = None

    def __post_init__(self):
        if self.capability_timeouts is None:
            self.capability_timeouts = {}

    def get_timeout(self, capability: str) -> float:
        """Get timeout for a specific capability"""
        return self.capability_timeouts.get(capability, self.default_timeout)


class TimeoutManager:
    """
    Manages intent execution timeouts.

    Features:
    - Per-capability timeout configuration
    - Automatic timeout detection
    - Timeout callback mechanism
    - Timeout metrics tracking
    """

    def __init__(self, config: Optional[TimeoutConfig] = None):
        self._config = config or TimeoutConfig()
        self._tracked_intents: Dict[str, float] = {}  # intent_id -> start_time
        self._lock = threading.Lock()
        self._timeout_callbacks: list[Callable[[Intent, float], None]] = []

        # Metrics
        self._total_timeouts = 0

        # Start monitoring thread
        self._running = True
        self._monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self._monitor_thread.start()

    def register_timeout_callback(self, callback: Callable[[Intent, float], None]):
        """
        Register a callback to be called when an intent times out.

        Args:
            callback: Function(intent, elapsed_time) to call on timeout
        """
        self._timeout_callbacks.append(callback)

    def start_tracking(self, intent: Intent):
        """Start tracking timeout for an intent"""
        with self._lock:
            self._tracked_intents[intent.intent_id] = time.time()

    def stop_tracking(self, intent_id: str):
        """Stop tracking timeout for an intent (completed successfully)"""
        with self._lock:
            if intent_id in self._tracked_intents:
                del self._tracked_intents[intent_id]

    def get_elapsed_time(self, intent_id: str) -> Optional[float]:
        """Get elapsed time for a tracked intent"""
        with self._lock:
            if intent_id not in self._tracked_intents:
                return None
            return time.time() - self._tracked_intents[intent_id]

    def check_timeout(self, intent: Intent) -> bool:
        """
        Check if an intent has timed out.

        Returns:
            True if timed out, False otherwise
        """
        with self._lock:
            if intent.intent_id not in self._tracked_intents:
                return False

            elapsed = time.time() - self._tracked_intents[intent.intent_id]
            timeout = self._config.get_timeout(intent.capability)

            return elapsed > timeout

    def _monitor_loop(self):
        """Background thread that monitors for timeouts"""
        while self._running:
            time.sleep(1.0)  # Check every second

            timed_out = []

            with self._lock:
                current_time = time.time()
                for intent_id, start_time in list(self._tracked_intents.items()):
                    elapsed = current_time - start_time
                    # We need the intent object to check capability timeout
                    # For now, use default timeout
                    if elapsed > self._config.default_timeout:
                        timed_out.append((intent_id, elapsed))

            # Call callbacks outside lock
            for intent_id, elapsed in timed_out:
                self._total_timeouts += 1
                # Note: We don't have the full intent object here
                # In practice, callbacks would need to look it up
                for callback in self._timeout_callbacks:
                    try:
                        callback(intent_id, elapsed)
                    except Exception as e:
                        print(f"Error in timeout callback: {e}")

    def get_metrics(self) -> dict:
        """Get timeout metrics"""
        with self._lock:
            return {
                "tracked_intents": len(self._tracked_intents),
                "total_timeouts": self._total_timeouts,
                "default_timeout": self._config.default_timeout
            }

    def shutdown(self):
        """Stop the monitoring thread"""
        self._running = False
        if self._monitor_thread.is_alive():
            self._monitor_thread.join(timeout=2.0)
