"""
Thread manager for coordinating multiple application components
"""

import time
import logging
import threading
from typing import Dict, Any, Optional, List, Callable
from abc import ABC, abstractmethod

from ..config.settings import AppSettings
from .message_bus import MessageBus, Message, MessageType

logger = logging.getLogger(__name__)


class ThreadedComponent(ABC):
    """Base class for threaded components"""
    
    def __init__(self, name: str, message_bus: MessageBus):
        self.name = name
        self.message_bus = message_bus
        self.running = False
        self.thread: Optional[threading.Thread] = None
        self.shutdown_event = threading.Event()
        self._last_heartbeat = time.time()
    
    @abstractmethod
    def initialize(self) -> bool:
        """Initialize component"""
        pass
    
    @abstractmethod
    def run(self):
        """Main component run loop"""
        pass
    
    @abstractmethod
    def shutdown(self):
        """Shutdown component"""
        pass
    
    def start(self) -> bool:
        """Start component in a separate thread"""
        try:
            if self.running:
                logger.warning(f"Component {self.name} is already running")
                return True
            
            # Initialize component
            if not self.initialize():
                logger.error(f"Failed to initialize component {self.name}")
                return False
            
            # Start thread
            self.thread = threading.Thread(
                target=self._thread_wrapper,
                name=f"{self.name}Thread",
                daemon=True
            )
            
            self.running = True
            self.shutdown_event.clear()
            self.thread.start()
            
            logger.info(f"Component {self.name} started successfully")
            return True
            
        except Exception as e:
            logger.error(f"Failed to start component {self.name}: {e}")
            return False
    
    def stop(self, timeout: float = 5.0) -> bool:
        """Stop component"""
        try:
            if not self.running:
                logger.warning(f"Component {self.name} is not running")
                return True
            
            logger.info(f"Stopping component {self.name}...")
            
            # Signal shutdown
            self.running = False
            self.shutdown_event.set()
            
            # Call component-specific shutdown
            self.shutdown()
            
            # Wait for thread to finish
            if self.thread and self.thread.is_alive():
                self.thread.join(timeout=timeout)
                
                if self.thread.is_alive():
                    logger.warning(f"Component {self.name} thread did not stop within timeout")
                    return False
            
            logger.info(f"Component {self.name} stopped successfully")
            return True
            
        except Exception as e:
            logger.error(f"Failed to stop component {self.name}: {e}")
            import traceback
            logger.error(f"Full traceback: {traceback.format_exc()}")
            return False
    
    def _thread_wrapper(self):
        """Thread wrapper with error handling"""
        try:
            logger.debug(f"Component {self.name} thread started")
            self.run()
        except Exception as e:
            logger.error(f"Component {self.name} thread failed: {e}")
            
            # Send error message
            try:
                error_message = Message(
                    type=MessageType.SYSTEM_ERROR,
                    sender=self.name,
                    data={
                        "error": str(e),
                        "component": self.name
                    }
                )
                self.message_bus.publish(error_message, broadcast=True)
            except:
                pass  # Don't let error reporting crash the thread
        finally:
            self.running = False
            logger.debug(f"Component {self.name} thread ended")
    
    def is_alive(self) -> bool:
        """Check if component thread is alive"""
        return self.thread is not None and self.thread.is_alive()
    
    def is_healthy(self) -> bool:
        """Check if component is healthy (heartbeat within last 30 seconds)"""
        return time.time() - self._last_heartbeat < 30.0
    
    def heartbeat(self):
        """Update heartbeat timestamp"""
        self._last_heartbeat = time.time()
    
    def get_status(self) -> Dict[str, Any]:
        """Get component status"""
        return {
            "name": self.name,
            "running": self.running,
            "thread_alive": self.is_alive(),
            "healthy": self.is_healthy(),
            "last_heartbeat": self._last_heartbeat
        }


class ThreadManager:
    """Manages multiple threaded components"""
    
    def __init__(self, message_bus: MessageBus, settings: AppSettings):
        self.message_bus = message_bus
        self.settings = settings
        self.components: Dict[str, ThreadedComponent] = {}
        self._lock = threading.RLock()
        
        logger.info("Thread manager initialized")
    
    def register_component(self, name: str, component: ThreadedComponent):
        """Register a component with the thread manager"""
        with self._lock:
            if name in self.components:
                logger.warning(f"Component {name} is already registered")
                return
            
            self.components[name] = component
            logger.info(f"Component {name} registered with thread manager")
    
    def unregister_component(self, name: str):
        """Unregister a component"""
        with self._lock:
            if name in self.components:
                component = self.components[name]
                
                # Stop component if running
                if component.running:
                    component.stop()
                
                del self.components[name]
                logger.info(f"Component {name} unregistered from thread manager")
    
    def start_component(self, name: str) -> bool:
        """Start a specific component"""
        with self._lock:
            if name not in self.components:
                logger.error(f"Component {name} not found")
                return False
            
            return self.components[name].start()
    
    def stop_component(self, name: str, timeout: float = 5.0) -> bool:
        """Stop a specific component"""
        with self._lock:
            if name not in self.components:
                logger.error(f"Component {name} not found")
                return False
            
            return self.components[name].stop(timeout)
    
    def start_all(self) -> bool:
        """Start all registered components"""
        logger.info("Starting all components...")
        
        success = True
        started_components = []
        
        with self._lock:
            for name, component in self.components.items():
                if component.start():
                    started_components.append(name)
                    logger.info(f"Component {name} started")
                else:
                    logger.error(f"Failed to start component {name}")
                    success = False
                    break
        
        if not success:
            # Stop already started components
            logger.error("Starting all components failed, stopping started components...")
            for name in started_components:
                self.stop_component(name)
            return False
        
        logger.info(f"All {len(started_components)} components started successfully")
        return True
    
    def stop_all(self, timeout: float = 10.0) -> bool:
        """Stop all components"""
        logger.info("Stopping all components...")

        success = True
        # Use at least 8 seconds per component, but distribute total timeout
        stop_timeout = max(timeout / max(len(self.components), 1), 8.0)

        with self._lock:
            for name, component in self.components.items():
                try:
                    if not component.stop(stop_timeout):
                        logger.error(f"Failed to stop component {name}")
                        success = False
                    else:
                        logger.info(f"Component {name} stopped")
                except Exception as e:
                    logger.error(f"Exception stopping component {name}: {e}")
                    success = False

        if success:
            logger.info("All components stopped successfully")
        else:
            logger.warning("Some components failed to stop cleanly")

        return success
    
    def restart_component(self, name: str, timeout: float = 5.0) -> bool:
        """Restart a specific component"""
        logger.info(f"Restarting component {name}")
        
        if self.stop_component(name, timeout):
            time.sleep(1)  # Brief pause
            return self.start_component(name)
        
        return False
    
    def is_component_running(self, name: str) -> bool:
        """Check if a component is running"""
        with self._lock:
            if name not in self.components:
                return False
            
            return self.components[name].running and self.components[name].is_alive()
    
    def is_component_healthy(self, name: str) -> bool:
        """Check if a component is healthy"""
        with self._lock:
            if name not in self.components:
                return False
            
            return self.components[name].is_healthy()
    
    def get_component_names(self) -> List[str]:
        """Get list of registered component names"""
        with self._lock:
            return list(self.components.keys())
    
    def get_running_components(self) -> List[str]:
        """Get list of currently running components"""
        with self._lock:
            return [name for name, comp in self.components.items() 
                   if comp.running and comp.is_alive()]
    
    def get_unhealthy_components(self) -> List[str]:
        """Get list of unhealthy components"""
        with self._lock:
            return [name for name, comp in self.components.items() 
                   if comp.running and not comp.is_healthy()]
    
    def get_component_status(self, name: str) -> Optional[Dict[str, Any]]:
        """Get status of a specific component"""
        with self._lock:
            if name not in self.components:
                return None
            
            return self.components[name].get_status()
    
    def get_all_component_status(self) -> Dict[str, Dict[str, Any]]:
        """Get status of all components"""
        with self._lock:
            return {name: comp.get_status() for name, comp in self.components.items()}
    
    def wait_for_shutdown(self, timeout: Optional[float] = None):
        """Wait for all component threads to finish"""
        logger.info("Waiting for component threads to finish...")
        
        with self._lock:
            threads = [(name, comp.thread) for name, comp in self.components.items() 
                      if comp.thread and comp.thread.is_alive()]
        
        if not threads:
            logger.info("No active threads to wait for")
            return
        
        # Calculate per-thread timeout
        per_thread_timeout = timeout / len(threads) if timeout else None
        
        for name, thread in threads:
            try:
                thread.join(timeout=per_thread_timeout)
                if thread.is_alive():
                    logger.warning(f"Thread for component {name} did not finish within timeout")
                else:
                    logger.debug(f"Thread for component {name} finished")
            except Exception as e:
                logger.error(f"Error waiting for thread {name}: {e}")
        
        logger.info("Finished waiting for component threads")
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get thread manager statistics"""
        with self._lock:
            total_threads = len(self.components)
            running_threads = len([c for c in self.components.values() 
                                 if c.running and c.is_alive()])
            healthy_threads = len([c for c in self.components.values() 
                                 if c.is_healthy()])
            
            return {
                "total_threads": total_threads,
                "running_threads": running_threads,
                "healthy_threads": healthy_threads,
                "unhealthy_threads": total_threads - healthy_threads,
                "components": {
                    name: {
                        "running": comp.running,
                        "alive": comp.is_alive(),
                        "healthy": comp.is_healthy()
                    }
                    for name, comp in self.components.items()
                }
            }
    
    def perform_health_check(self) -> Dict[str, Any]:
        """Perform comprehensive health check on all components"""
        logger.info("Performing component health check...")
        
        health_report = {
            "timestamp": time.time(),
            "overall_healthy": True,
            "components": {},
            "issues": []
        }
        
        with self._lock:
            for name, component in self.components.items():
                comp_status = component.get_status()
                health_report["components"][name] = comp_status
                
                # Check for issues
                if component.running and not component.is_alive():
                    issue = f"Component {name} is marked as running but thread is dead"
                    health_report["issues"].append(issue)
                    health_report["overall_healthy"] = False
                    logger.warning(issue)
                
                if component.running and not component.is_healthy():
                    issue = f"Component {name} is unhealthy (no recent heartbeat)"
                    health_report["issues"].append(issue)
                    health_report["overall_healthy"] = False
                    logger.warning(issue)
        
        if health_report["overall_healthy"]:
            logger.info("All components are healthy")
        else:
            logger.warning(f"Health check found {len(health_report['issues'])} issues")
        
        return health_report
    
    def cleanup(self):
        """Cleanup thread manager resources"""
        logger.info("Cleaning up thread manager...")
        
        # Stop all components
        self.stop_all(timeout=5.0)
        
        # Wait for threads to finish
        self.wait_for_shutdown(timeout=10.0)
        
        # Clear components
        with self._lock:
            self.components.clear()
        
        logger.info("Thread manager cleanup completed")