import time
import logging
from typing import List, Callable, Tuple, Dict, Optional

class SwarmHealthMonitor:
    def __init__(self, agents: List[object], check_interval: int = 30, stall_threshold: int = 60, alerting_callback: Optional[Callable[[str], None]] = None):
        self.agents = agents
        self.check_interval = check_interval
        self.stall_threshold = stall_threshold
        self.alerting_callback = alerting_callback or self._default_alert
        self.agent_status: Dict[str, Tuple[bool, str]] = {}
        self.logger = logging.getLogger(__name__)
        logging.basicConfig(level=logging.INFO)

    def _default_alert(self, message: str):
        self.logger.error(f'SWARM ALERT: {message}')

    def _check_agent_health(self, agent: object) -> Tuple[bool, str]:
        try:
            heartbeat = agent.get_last_heartbeat()
            if heartbeat is None:
                return False, 'No heartbeat'
            current_time = time.time()
            if current_time - heartbeat > self.stall_threshold:
                return False, f'Stalled (last heartbeat: {heartbeat:.2f})'
            return True, 'Healthy'
        except Exception as e:
            return False, f'Health check error: {str(e)}'

    def _restart_agent(self, agent: object) -> Tuple[bool, str]:
        try:
            agent.restart()
            return True, 'Restart successful'
        except Exception as e:
            return False, f'Restart failed: {str(e)}'

    def start(self):
        self.logger.info('Starting Swarm Health Monitor')
        try:
            while True:
                for agent in self.agents:
                    agent_id = getattr(agent, 'id', 'unknown')
                    is_healthy, reason = self._check_agent_health(agent)
                    self.agent_status[agent_id] = (is_healthy, reason)
                    
                    if not is_healthy:
                        self.logger.warning(f'Restarting stalled agent {agent_id} ({reason})')
                        success, restart_msg = self._restart_agent(agent)
                        
                        if success:
                            self.logger.info(f'Agent {agent_id} restarted: {restart_msg}')
                        else:
                            self.logger.error(f'Agent {agent_id} restart failed: {restart_msg}')
                            self.alerting_callback(f'Agent {agent_id} restart failed: {restart_msg}')
                
                time.sleep(self.check_interval)
        except KeyboardInterrupt:
            self.logger.info('Swarm Health Monitor stopped by user')
        except Exception as e:
            self.logger.exception('Unexpected error in health monitor')
            self.alerting_callback(f'Health monitor critical failure: {str(e)}')

    def get_status(self) -> Dict[str, Tuple[bool, str]]:
        return self.agent_status

if __name__ == '__main__':
    # Example agent implementation for demonstration
    class ExampleAgent:
        def __init__(self, agent_id: str):
            self.id = agent_id
            self.last_heartbeat = time.time()
            self.restarts = 0

        def get_last_heartbeat(self) -> float:
            return self.last_heartbeat

        def restart(self):
            self.last_heartbeat = time.time()
            self.restarts += 1
            self.logger.info(f'Restarted agent {self.id}, total restarts: {self.restarts}')

    # Example usage
    agents = [ExampleAgent('agent1'), ExampleAgent('agent2')]
    monitor = SwarmHealthMonitor(agents, check_interval=5, stall_threshold=10)
    
    # Simulate heartbeat updates
    def simulate_heartbeat(agents):
        while True:
            for agent in agents:
                agent.last_heartbeat = time.time()
                time.sleep(1)
    
    import threading
    threading.Thread(target=simulate_heartbeat, args=(agents,), daemon=True).start()
    
    monitor.start()