"""Agent Maestro NASA - Base class for FEID-MAS agents."""

# Centralized imports
from .imports import *

# CONFIGURACIÓN DE LOGGING PROFESIONAL
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s | [%(levelname)s] | %(message)s',
    handlers=[logging.StreamHandler(sys.stdout)]
)

# ============================================================================
# AGENT BASE CLASS
# ============================================================================

class AgenteMaestroNASA(ABC):
    def __init__(
        self,
        name: str,
        config: Optional[ConfigAgente] = None,
        handler: Optional[Callable] = None,
        auto_persist: bool = True,
        retry: Optional[RetryStrategy] = None,
    ):
        """Inicializa el agente base con configuración flexible.

        Args:
            name: Identificador único del agente.
            config: Objeto ConfigAgente con todos los parámetros (opcional).
            handler: Callback para persistencia externa.
            auto_persist: Habilitar persistencia local en JSONL.
            retry: Estrategia personalizada de reintentos.
        """
        # --- RESOLUCIÓN DE CONFIGURACIÓN ---
        if config is None:
            config = ConfigAgente()
        
        # Validar configuración antes de usarla
        try:
            config.validar()
        except ValueError as e:
            logging.error(f"Configuración inválida: {e}")
            raise
        
        # Guardar referencia a config
        self.config = config
        
        # Apply values from config
        self.capacity = config.capacidad
        self.max_retries = config.max_reintentos
        self.backpressure_threshold = config.backpressure_threshold
        self.default_ttl = config.ttl_defecto
        
        # --- BASE ATTRIBUTES ---
        self.name = name
        self.inbox = PriorityQueue()
        self.alive = True
        self.state = "IDLE"
        self._last_activity = time.time()
        
        # Thread references (will be populated by AgentRuntime)
        self.worker_thread = None
        self.perception_thread = None

        # --- EXTENSION CONFIGURATION ---
        self.external_handler = handler
        self.auto_persist = auto_persist
        self.lock = threading.Lock()
        self.executor = (
            ThreadPoolExecutor(max_workers=config.max_workers) if config.max_workers else None
        )

        # --- ENTERPRISE FEATURES ---
        # Correlation IDs for distributed tracing (always enabled - core feature)
        self.correlation_tracker = CorrelationTracker()
        
        # Circuit breaker for fault tolerance (conditional)
        self.circuit_breaker = (
            CircuitBreaker(failure_threshold=config.circuit_breaker_threshold)
            if config.enable_circuit_breaker
            else None
        )
        
        # Customizable retry strategy (always enabled - core feature)
        self.retry_strategy = retry or ExponentialBackoffStrategy()
        
        # Lifecycle hooks (always enabled - core feature)
        self.lifecycle_hooks = LifecycleHooks()
        
        # Payload validator (always enabled - core feature)
        self.payload_validator = ValidadorPayload(max_bytes=config.payload_max_bytes)
        
        # Rate limiter (conditional)
        self.rate_limiter = (
            RateLimiter(
                tasa_tokens=config.rate_limit_tokens,
                capacidad_max=config.rate_limit_capacidad
            )
            if config.enable_rate_limiter
            else None
        )
        
        # Performance metrics collector (always enabled - core feature)
        self.metrics_collector = ColectorMetricas(ventana_segundos=config.metricas_ventana_segundos)
        
        # Queue policy manager (always enabled - core feature)
        self.queue_policy_manager = GestorColaPolitica(politica=config.politica_cola)
        
        # Structured auditor (conditional)
        self.auditor = (
            AuditorEstructurado(nombre_agente=name)
            if config.enable_auditor
            else None
        )
        
        # Error policy manager (always enabled - core feature)
        self.error_policy_manager = GestorPoliticasError(politica_defecto=config.politica_error_defecto)
        
        # Anti-starvation manager (conditional)
        self.anti_starvation_manager = (
            GestorAntiInanicion(
                ratio_minimo_baja_prioridad=config.anti_inanicion_ratio_minimo
            )
            if config.enable_anti_starvation
            else None
        )
        
        # Security sandbox with risky command denylist (conditional)
        self.security_sandbox = (
            SandboxSeguridad(
                habilitar_whitelist=config.sandbox_habilitar_whitelist
            )
            if config.enable_sandbox
            else None
        )
        
        # Health probe (Kubernetes-style health probes) (conditional)
        self.health_probe = (
            SondeoSalud(
                nombre_agente=name,
                umbral_inactividad=config.sondeo_umbral_inactividad
            )
            if config.enable_health_probes
            else None
        )

        # --- MEMORY PROTECTION (RAM) ---
        # Using deque to prevent infinite list growth if agent runs for months
        self.max_memory = config.max_memoria
        self.outbox = deque(maxlen=self.max_memory)
        self.incident_log = deque(maxlen=self.max_memory)
        self.dead_letter_queue = deque(maxlen=self.max_memory)

        # --- AUTOMATIC PERSISTENCE (ROTATION) ---
        self.max_records_per_file = config.max_registros_por_archivo
        self.record_counter = 0
        self.log_version = 1
        self.current_log_file = f"auto_log_{self.name}_v{self.log_version}.json"

        # --- COMPONENTS ---
        self.protocol_adapter = ProtocolAdapter(self.send_task)
        self.event_sink = EventSink(self)
        self.queue_manager = QueueManager(
            self.inbox,
            capacity_getter=lambda: self.capacity,
            backpressure_threshold=self.backpressure_threshold
        )
        self.task_processor = TaskProcessor(
            self.circuit_breaker,
            self.retry_strategy,
            self.metrics_collector,
            self.auditor,
            executor=self.executor,
            max_retries=self.max_retries
        )
        self.agent_runtime = AgentRuntime(
            self.name,
            self._operational_cycle,
            self._perception_engine,
            self.lifecycle_hooks,
            self.executor
        )
        
        # --- FACADES (Simplified Interfaces) ---
        self.metrics = MetricsFacade(self.metrics_collector)
        self.security = SecurityFacade(self.security_sandbox) if self.security_sandbox else None
        self.health = HealthFacade(self.health_probe) if self.health_probe else None

        # --- METRICS ---
        self.global_metrics = {
            "successes": 0,
            "recovered_failures": 0,
            "fatal_failures": 0,
            "rejected": 0,
            "ttl_deaths": 0,
        }

        # Perception block
        self.perception_frequency = config.frecuencia_percepcion
        
        # --- RUNTIME START ---
        self.agent_runtime.start()
        
        # --- INVOKE on_start HOOK ---
        # Note: on_start hook is already called by agent_runtime.start()


    # --- PERSISTENCE AND EVENTS SYSTEM ---

    def _register_to_auto_file(self, event_type, data):
        """Writes to disk record by record if enabled."""
        self.event_sink.register_to_auto_file(event_type, data)

    def _dispatch_event(self, event_type, data):
        """Sends event to RAM, Disk and External Handler."""
        self.event_sink.dispatch_event(event_type, data)

    # --- MULTI-PROTOCOL COMMUNICATION LAYER ---

    def receive_message(self, message, priority=2, complexity=5, ttl=10):
        """Decodes FIPA, KQML, JSON-RPC and Simple Text.

        Returns:
            Task ID in queue.
        """
        return self.protocol_adapter.receive_message(message, priority, complexity, ttl)

    def send_task(
        self,
        task,
        priority=2,
        complexity=5,
        ttl=10,
        m_id=None,
        protocol="SIMPLE",
        backpressure=False,
        max_backpressure_delay=0.5,
        correlation_id: Optional[str] = None,
    ):
        """Validates saturation and risk before queuing.

        Args:
            correlation_id: Correlation ID for distributed tracing (optional).
        
        Raises:
            ValueError: if parameters are invalid.
        """
        if task is None:
            raise ValueError("Task cannot be None")
        if not isinstance(priority, int) or not 0 <= priority <= 5:
            raise ValueError("Priority must be an integer between 0 and 5")
        if not isinstance(complexity, int) or not 1 <= complexity <= 10:
            raise ValueError("Complexity must be an integer between 1 and 10")
        if not isinstance(ttl, (int, float)) or ttl <= 0:
            raise ValueError("TTL must be a number greater than 0")
        
        # Validate payload
        try:
            self.payload_validator.validar(task)
        except (TypeError, ValueError) as e:
            logging.error(f"Payload validation failed: {e}")
            self.global_metrics["rejected"] += 1
            return None
        
        # Rate limiting check
        origin = m_id or "default"
        if self.rate_limiter and not self.rate_limiter.permitir_por_origen(origin, costo=1.0):
            logging.warning(f"Rate limit exceeded for origin: {origin}")
            self._register_incident("RATE_LIMIT", task, "Limited by rate", m_id)
            self.global_metrics["rejected"] += 1
            return None
        
        # Check queue capacity
        if not self.queue_manager.validate_capacity():
            self._register_incident("SATURATION", task, "Queue full", m_id)
            return None
        
        # Apply backpressure if requested
        current_load = self.queue_manager.get_queue_load()
        if backpressure:
            delay = self.queue_manager.apply_backpressure(max_backpressure_delay)
            if delay > 0:
                time.sleep(delay)
        
        # Security evaluation
        if (complexity > 7 and current_load > 0.8) or any(
            p in task.lower() for p in ["shutdown", "format", "delete_all"]
        ):
            self._register_incident("SECURITY", task, "Risk detected", m_id)
            return None

        final_id = m_id if m_id else str(uuid.uuid4())[:8]
        
        # Create or track correlation ID for distributed tracing
        if correlation_id:
            self.correlation_tracker.create_correlation(correlation_id)
        else:
            correlation_id = self.correlation_tracker.create_correlation()
        
        # Register in audit
        if self.auditor:
            self.auditor.registrar_tarea_enviada(final_id, priority, ttl)
        
        # Enqueue task using queue manager
        self.queue_manager.enqueue_task(
            priority, ttl, complexity, final_id, task, protocol, correlation_id
        )
        return final_id

    def cancel_task(self, m_id):
        """Cancels a queued task by its ID.

        Returns:
            True if removed, False if didn't exist.
        """
        return self.queue_manager.cancel_task(m_id)

    # --- PROCESSING ENGINE ---

    def _operational_cycle(self):
        """Main task processing loop with circuit breaker and retry strategy."""
        logging.info(f"AGENT {self.name}: NASA Industrial Mode Started.")
        while self.alive:
            try:
                # Unpack task with correlation_id
                prio, t_arrival, ttl, comp, attempts, m_id, task, proto, corr_id = self.inbox.get(timeout=1)
                
                self.last_activity = time.time()

                # Check TTL expiration
                if not self.task_processor.check_ttl(t_arrival, ttl, m_id, corr_id):
                    self._register_incident("TTL_EXPIRED", task, "Expiration", m_id)
                    with self.lock:
                        self.global_metrics["ttl_deaths"] += 1
                    self.task_processor.record_ttl_expiration(m_id)
                    self.inbox.task_done()
                    continue

                self.state = "WORKING"
                t_start = time.time()
                
                # Register task start in metrics
                self.metrics_collector.registrar_inicio_tarea(m_id)

                # Execute task with circuit breaker
                def execute_work():
                    return self._technical_work(task)
                
                success, result = self.task_processor.execute_task(m_id, task, execute_work, corr_id)
                
                if success:
                    # Task completed successfully
                    receipt = self._create_polyglot_receipt(
                        m_id, "COMPLETE", result, t_arrival, t_start, proto, corr_id
                    )
                    self._dispatch_event("SUCCESS", receipt)
                    with self.lock:
                        self.global_metrics["successes"] += 1
                    
                    self.task_processor.record_success(m_id, t_start, corr_id)
                    self.inbox.task_done()
                else:
                    # Task failed, check if should retry
                    error = result
                    should_retry = self.task_processor.should_retry(error, attempts)
                    
                    if should_retry:
                        delay = self.task_processor.get_retry_delay(attempts)
                        if corr_id:
                            logging.debug(f"[TRACE:{corr_id}] Retrying {m_id} in {delay:.2f}s (attempt {attempts+1}/{self.max_retries})")
                        
                        self.task_processor.requeue_task(
                            self.inbox, prio, t_arrival, ttl, comp, attempts,
                            m_id, task, proto, corr_id, delay
                        )
                        with self.lock:
                            self.global_metrics["recovered_failures"] += 1
                        self.inbox.task_done()
                    else:
                        # Fatal failure
                        receipt = self._create_polyglot_receipt(
                            m_id, "ERROR_FATAL", str(error), t_arrival, t_start, proto, corr_id
                        )
                        self._dispatch_event("INCIDENT", receipt)
                        with self.lock:
                            self.dead_letter_queue.append(receipt)
                        with self.lock:
                            self.global_metrics["fatal_failures"] += 1
                        
                        self.task_processor.record_failure(m_id, error, corr_id)
                        self.inbox.task_done()

                self.state = "IDLE"
            except Empty:
                continue


    # --- ABSTRACT METRIC ---
    @abstractmethod
    def _technical_work(self, task):
        """This method MUST be implemented by the child agent."""
        raise NotImplementedError(
            "Child agent must define its own logic in _technical_work"
        )

    def _create_polyglot_receipt(self, m_id, status, result, t_arrival, t_start, proto, correlation_id=None):
        """Encapsulates response in original protocol with correlation ID."""
        return self.protocol_adapter.create_receipt(
            m_id, status, result, t_arrival, t_start, proto, correlation_id
        )

    def _register_incident(self, incident_type, task, cause, m_id=None):
        """Centralizes error and rejection logging."""
        data = {
            "type": incident_type,
            "mission_id": m_id,
            "task": task,
            "cause": cause,
            "ts": time.ctime(),
        }
        self._dispatch_event("INCIDENT", data)
        if incident_type in ["SATURATION", "SECURITY"]:
            with self.lock:
                self.global_metrics["rejected"] += 1

    # --- DIAGNOSTIC METHODS (Consistent with Tests) ---

    def get_operational_summary(self):
        """Returns agent operational metrics."""
        with self.lock:
            return {
                "agent": self.name,
                "current_state": self.state,
                "successful_tasks": self.global_metrics["successes"],
                "total_rejected_tasks": self.global_metrics["rejected"],
                "ttl_expired_tasks": self.global_metrics["ttl_deaths"],
                "successful_recoveries": self.global_metrics[
                    "recovered_failures"
                ],
                "pending_tasks": self.inbox.qsize(),
            }

    def get_mission_details(self, category="successes", limit=None):
        """Returns detailed in-memory lists.

        Args:
            category: "successes", "incidents" or "queue".
            limit: maximum number of elements to return (from the end).
        """
        with self.lock:
            if category == "successes":
                if limit is not None:
                    return list(deque(self.outbox, maxlen=limit))
                return list(self.outbox)
            if category == "incidents":
                if limit is not None:
                    return list(deque(self.incident_log, maxlen=limit))
                return list(self.incident_log)
            if category == "queue":
                if limit is not None:
                    return [t[-1] for t in list(deque(self.inbox.queue, maxlen=limit))]
                return [t[-1] for t in list(self.inbox.queue)]
            if category == "dead_letter":
                if limit is not None:
                    return list(deque(self.dead_letter_queue, maxlen=limit))
                return list(self.dead_letter_queue)
            return {"error": "Invalid category"}

    # --- ENTERPRISE FEATURES API ---
    
    def get_correlation(self, correlation_id: str) -> Dict[str, Any]:
        """Gets correlation status and delegation chain.
        
        Args:
            correlation_id: Correlation ID to track.
        
        Returns:
            Dictionary with metadata and delegation chain.
        """
        return self.correlation_tracker.get_metadata(correlation_id)
    
    def get_circuit_state(self) -> str:
        """Gets current circuit breaker state.
        
        Returns:
            "closed", "open", or "half_open", or "disabled" if not enabled.
        """
        return self.circuit_breaker.get_state() if self.circuit_breaker else "disabled"
    
    def get_retry_strategy(self) -> str:
        """Gets retry strategy class in use.
        
        Returns:
            Strategy class name.
        """
        return self.retry_strategy.__class__.__name__

    def monitor_health(self):
        """Quick vitality status."""
        inactivity = time.time() - self.last_activity
        return {
            "agent": self.name,
            "state": self.state,
            "inactive_for": round(inactivity, 2),
            "circuit_breaker": self.get_circuit_state(),
            "retry_strategy": self.get_retry_strategy(),
        }

    # --- SYSTEM SHUTDOWN ---

    def shutdown(self):
        """Saves final reports and stops agent."""
        logging.info(f"AGENT {self.name}: Saving final logs...")

        with self.lock:
            success_list = list(self.outbox)
            incident_list = list(self.incident_log)

        try:
            # Save the two requested files
            with open(f"successes_{self.name}.json", "w") as f:
                json.dump(success_list, f, indent=4)
            with open(f"incidents_{self.name}.json", "w") as f:
                json.dump(incident_list, f, indent=4)

            self.alive = False
            if self.executor:
                self.executor.shutdown(wait=False, cancel_futures=True)
            logging.info(f"AGENT {self.name}: Shutdown successful.")
        except Exception as e:
            logging.error(f"Error saving final logs: {e}")

    def send_to_agent(self, target_agent, message, priority=2):
        """Allows assigning a task to another fleet agent."""
        logging.info(f"🔄 {self.name} delegating task to {target_agent.name}")
        return target_agent.receive_message(message, priority=priority)

    def _perception_engine(self):
        while self.alive:
            # If queue is very full, wait to avoid saturation
            logging.debug(
                "Agent %s: perception cycle", self.name
            )
            if self.inbox.qsize() < (self.capacity / 2):
                try:
                    self.perceive_environment()
                except Exception as e:
                    logging.error("Error in perception: %s", e)

            # "Go to sleep" logic if no activity
            if self.state == "IDLE":
                time.sleep(self.perception_frequency * 2)  # Sleep more if idle
            else:
                time.sleep(self.perception_frequency)

    def perceive_environment(self):
        """Optional method: child decides HOW to look at the world."""
        return None

    @property
    def last_activity(self):
        with self.lock:
            return self._last_activity

    @last_activity.setter
    def last_activity(self, value):
        with self.lock:
            self._last_activity = value    
    # --- LIFECYCLE HOOKS ---
    
    def register_hook(self, event: str, callback: Callable):
        """Registers callback for lifecycle event.
        
        Available events:
            - on_start: Agent started
            - on_stop: Agent stopped
            - on_error: Unrecoverable error
        """
        try:
            self.lifecycle_hooks.registrar(event, callback)
        except ValueError as e:
            logging.error(f"Error registering hook: {e}")
    
    def invoke_custom_hook(self, hook_name: str, *args, **kwargs):
        """Invokes registered hooks (internal use)."""
        self.lifecycle_hooks.invocar(hook_name, *args, **kwargs)
    
    def configure_rate_limit(self, token_rate: float, max_capacity: float):
        """Configures global rate limiter."""
        if self.rate_limiter:
            self.rate_limiter.tasa_tokens = token_rate
            self.rate_limiter.capacidad_max = max_capacity
    
    def add_origin_limit(self, origin: str, token_rate: float, max_capacity: float):
        """Adds specific limit for an origin."""
        if self.rate_limiter:
            self.rate_limiter.registrar_limite_origen(origin, token_rate, max_capacity)
    
    def get_rate_limiter_state(self) -> Dict[str, Any]:
        """Returns rate limiter state."""
        return self.rate_limiter.obtener_estado() if self.rate_limiter else {}
    
    def get_metrics(self) -> Dict[str, Any]:
        """Returns performance metrics."""
        return self.metrics_collector.obtener_estado()
    
    def get_avg_latency(self) -> float:
        """Returns average latency in seconds."""
        return self.metrics_collector.obtener_latencia_promedio()
    
    def get_p95_latency(self) -> float:
        """Returns 95th percentile latency."""
        return self.metrics_collector.obtener_latencia_p95()
    
    def get_throughput(self) -> float:
        """Returns throughput: tasks per second."""
        return self.metrics_collector.obtener_throughput()
    
    def get_error_rate(self) -> float:
        """Returns error rate (0.0 to 1.0)."""
        return self.metrics_collector.obtener_tasa_error()
    
    def clear_metrics(self):
        """Clears all collected metrics."""
        self.metrics_collector.limpiar()
    
    def change_queue_policy(self, policy: PoliticaCola):
        """Changes queue dispatch policy."""
        self.queue_policy_manager.cambiar_politica(policy)
    
    def get_queue_policy(self) -> str:
        """Returns current queue policy."""
        return self.queue_policy_manager.politica.value
    
    def get_audit_logs(self, last_n: int = 100) -> list:
        """Returns last N audit records."""
        return self.auditor.obtener_registros(last_n) if self.auditor else []
    
    def get_audit_logs_by_event(self, event: str) -> list:
        """Returns audit records filtered by event."""
        return self.auditor.obtener_registros_por_evento(event) if self.auditor else []
    
    def export_audit_json(self) -> str:
        """Exports audit as JSON."""
        return self.auditor.exportar_json() if self.auditor else "{}"
    
    def configure_default_error_policy(self, policy: PoliticaError):
        """Configures default error policy."""
        self.error_policy_manager.politica_defecto = policy
        logging.info(f"Default error policy: {policy.value}")
    
    def register_error_policy_by_type(self, error_type: str, policy: PoliticaError):
        """Registers specific policy for error type."""
        self.error_policy_manager.registrar_politica_por_tipo(error_type, policy)
    
    def get_error_policy(self, error_type: str) -> str:
        """Returns configured policy for an error type."""
        policy = self.error_policy_manager.obtener_politica(error_type)
        return policy.value
    
    def get_anti_starvation_stats(self) -> Dict[str, Any]:
        """Returns starvation protection statistics."""
        return self.anti_starvation_manager.obtener_estadisticas() if self.anti_starvation_manager else {}
    
    def set_min_low_priority_ratio(self, ratio: float):
        """Sets minimum ratio for low priority tasks (0.0 to 1.0)."""
        if self.anti_starvation_manager:
            ratio = max(0.0, min(1.0, ratio))
            self.anti_starvation_manager.ratio_minimo = ratio
            logging.info(f"Minimum low priority ratio set: {ratio}")
    
    def clear_anti_starvation_stats(self):
        """Clears anti-starvation manager statistics."""
        if self.anti_starvation_manager:
            self.anti_starvation_manager.limpiar()
    
    def add_denylist_pattern(self, pattern: str):
        """Adds forbidden pattern to security sandbox."""
        if self.security_sandbox:
            self.security_sandbox.agregar_denylist(pattern)
    
    def add_whitelist_pattern(self, pattern: str):
        """Adds allowed pattern to security sandbox."""
        if self.security_sandbox:
            self.security_sandbox.agregar_whitelist(pattern)
    
    def validate_safe_task(self, task: str) -> bool:
        """Validates if a task is safe according to sandbox policies."""
        return self.security_sandbox.es_segura_tarea(task) if self.security_sandbox else True
    
    def get_security_stats(self) -> Dict[str, Any]:
        """Gets security validation statistics."""
        return self.security_sandbox.obtener_estadisticas_seguridad() if self.security_sandbox else {}
    
    def enable_whitelist_mode(self, enable: bool = True):
        """Enables or disables whitelist mode in sandbox."""
        if self.security_sandbox:
            self.security_sandbox.habilitar_whitelist = enable
            mode = "whitelist" if enable else "denylist"
            logging.info(f"Sandbox mode changed to: {mode}")
    
    def clear_security_stats(self):
        """Clears security counters."""
        if self.security_sandbox:
            self.security_sandbox.limpiar_estadisticas()
    
    def liveness_probe(self) -> Dict[str, Any]:
        """Gets agent liveness state (is it alive?)."""
        return self.health_probe.sondeo_liveness() if self.health_probe else {"status": "healthy", "message": "Health probes disabled"}
    
    def readiness_probe(self) -> Dict[str, Any]:
        """Gets agent readiness state (is it ready?)."""
        if not self.health_probe:
            return {"status": "ready", "message": "Health probes disabled"}
        return self.health_probe.sondeo_readiness(
            self.alive,
            self.inbox.qsize(),
            self.capacity
        )
    
    def startup_probe(self) -> Dict[str, Any]:
        """Gets agent startup state (did it finish initialization?)."""
        return self.health_probe.sondeo_startup(self.alive) if self.health_probe else {"status": "started", "message": "Health probes disabled"}
    
    def get_general_health_state(self) -> Dict[str, Any]:
        """Gets general health state of agent."""
        return self.health_probe.obtener_estado_general(
            self.alive,
            self.inbox.qsize(),
            self.capacity
        )
    
    def graceful_shutdown(self, timeout: float = 30.0) -> Dict[str, Any]:
        """
        Stops agent gracefully (graceful shutdown).
        
        Steps:
        1. Stop signal (alive=False)
        2. Wait for queue to empty (until timeout)
        3. Wait for threads to finish
        4. Save final state
        5. Clean resources
        
        Args:
            timeout: Maximum seconds to wait (default 30s)
        
        Returns:
            Dict with shutdown statistics
        """
        # Signal stop
        self.alive = False
        
        # Delegate to agent_runtime for lifecycle management
        stats = self.agent_runtime.graceful_shutdown(
            self.inbox,
            timeout=timeout,
            metrics=self.global_metrics,
            outbox=self.outbox,
            incident_log=self.incident_log,
            health_probe=self.health_probe,
            auditor=self.auditor,
            metrics_collector=self.metrics_collector,
            alive_flag_getter=lambda: self.alive,
            capacity=self.capacity
        )
        
        # Add dead_letter_queue size to stats
        stats["dead_letter_size"] = len(self.dead_letter_queue)
        
        logging.info(f"[SHUTDOWN] {self.name}: Shutdown completed in {stats['shutdown_seconds']}s")
        return stats