"""
Health Monitoring System
Emits health snapshots every 60 seconds for performance diagnostics.
"""

import json
import time
import threading
import psutil
from typing import Dict, Any, Optional
from datetime import datetime
from ws_package.log.writer import LogWriter
from ws_package.thread_manager import thread_controls, _worker_threads, _worker_lock, get_worker_count
from ws_package.utils.db_write_queue import db_write_queue
from ws_package.log.thread_logger import thread_log_manager

logger = LogWriter.file(__name__)

# Track last lifecycle events
_last_lifecycle_events = {
    'last_sleep_time': None,
    'last_wake_time': None,
    'last_reconnect_time': None,
    'last_restart_time': None,
}
_lifecycle_lock = threading.Lock()


def record_lifecycle_event(event_type: str, timestamp: Optional[float] = None):
    """
    Record a lifecycle event (sleep, wake, reconnect, restart).
    
    Args:
        event_type: Type of event ('sleep', 'wake', 'reconnect', 'restart')
        timestamp: Optional timestamp (defaults to current time)
    """
    if timestamp is None:
        timestamp = time.time()
    
    with _lifecycle_lock:
        if event_type == 'sleep':
            _last_lifecycle_events['last_sleep_time'] = timestamp
        elif event_type == 'wake':
            _last_lifecycle_events['last_wake_time'] = timestamp
        elif event_type == 'reconnect':
            _last_lifecycle_events['last_reconnect_time'] = timestamp
        elif event_type == 'restart':
            _last_lifecycle_events['last_restart_time'] = timestamp


def get_last_lifecycle_event() -> Dict[str, Optional[float]]:
    """Get the last lifecycle events."""
    with _lifecycle_lock:
        return _last_lifecycle_events.copy()


def get_queue_sizes() -> Dict[str, int]:
    """
    Get sizes of all queues in the system.
    
    Returns:
        Dictionary mapping queue names to their sizes
    """
    queue_sizes = {}
    
    try:
        # Database write queue sizes
        db_queue_stats = db_write_queue.get_stats()
        queue_sizes['db_write_total'] = db_write_queue.get_total_queue_size()
        queue_sizes['db_write_by_type'] = db_queue_stats.get('queue_sizes', {})
        
        # Log queue size
        if thread_log_manager and hasattr(thread_log_manager, 'log_queue'):
            try:
                queue_sizes['log_queue'] = thread_log_manager.log_queue.qsize()
            except Exception:
                queue_sizes['log_queue'] = -1  # Error getting size
        else:
            queue_sizes['log_queue'] = 0
        
    except Exception as e:
        logger.debug("Error getting queue sizes: %s", e)
        queue_sizes['error'] = str(e)
    
    return queue_sizes


def health_snapshot() -> Dict[str, Any]:
    """
    Generate a health snapshot of the system.
    
    Returns:
        Dictionary containing system health metrics
    """
    try:
        process = psutil.Process()
        
        # Get thread information
        all_threads = threading.enumerate()
        thread_names = [t.name for t in all_threads]
        
        # Get worker thread information
        with _worker_lock:
            worker_names = list(_worker_threads.keys())
            active_workers = sum(1 for w in _worker_threads.values() if w.is_alive())
        
        # Get main thread controls
        main_thread_names = list(thread_controls.keys())
        active_main_threads = sum(
            1 for info in thread_controls.values()
            if info.get('thread') and info['thread'].is_alive()
        )
        
        # Get queue sizes
        queue_sizes = get_queue_sizes()
        
        # Get lifecycle events
        lifecycle_events = get_last_lifecycle_event()
        
        # Calculate time since last events
        current_time = time.time()
        time_since_events = {}
        for event_name, event_time in lifecycle_events.items():
            if event_time:
                time_since_events[f"{event_name}_seconds_ago"] = current_time - event_time
            else:
                time_since_events[f"{event_name}_seconds_ago"] = None
        
        snapshot = {
            "ts": int(time.time()),
            "timestamp_iso": datetime.now().isoformat(),
            "cpu_percent": process.cpu_percent(interval=0),
            "rss_mb": process.memory_info().rss / 1024 / 1024,
            "vms_mb": process.memory_info().vms / 1024 / 1024,
            "thread_count": len(all_threads),
            "thread_names": thread_names,
            "main_thread_count": active_main_threads,
            "main_thread_names": main_thread_names,
            "worker_thread_count": active_workers,
            "worker_thread_names": worker_names,
            "total_worker_count": get_worker_count(),
            "queue_sizes": queue_sizes,
            "last_lifecycle_event": lifecycle_events,
            "time_since_lifecycle_events": time_since_events,
        }
        
        # Add process-specific metrics if available
        try:
            snapshot["num_fds"] = process.num_fds() if hasattr(process, 'num_fds') else None
            snapshot["num_threads"] = process.num_threads() if hasattr(process, 'num_threads') else None
        except (AttributeError, psutil.AccessDenied):
            pass
        
        return snapshot
        
    except Exception as e:
        logger.error("Error generating health snapshot: %s", e, exc_info=True)
        return {
            "ts": int(time.time()),
            "error": str(e),
            "timestamp_iso": datetime.now().isoformat()
        }


class HealthMonitor:
    """
    Health monitoring service that emits health snapshots periodically.
    """
    
    def __init__(self, interval_seconds: int = 60):
        """
        Initialize health monitor.
        
        Args:
            interval_seconds: Interval between health snapshots (default: 60)
        """
        self.interval_seconds = interval_seconds
        self._monitor_thread: Optional[threading.Thread] = None
        self._stop_event = threading.Event()
        self._is_running = False
    
    def start(self):
        """Start the health monitoring thread."""
        if self._is_running:
            logger.warning("Health monitor is already running")
            return
        
        # Stop existing monitor if running
        if self._monitor_thread and self._monitor_thread.is_alive():
            logger.debug("Stopping existing health monitor thread before starting new one")
            self._stop_event.set()
            try:
                self._monitor_thread.join(timeout=2)
            except Exception as e:
                logger.debug("Error stopping existing health monitor thread: %s", e)
        
        self._stop_event.clear()
        self._is_running = True
        self._monitor_thread = threading.Thread(
            target=self._monitor_loop,
            name="HealthMonitor",
            daemon=True
        )
        self._monitor_thread.start()
        logger.info("Health monitor started (interval: %ss)", self.interval_seconds)
    
    def stop(self):
        """Stop the health monitoring thread."""
        if not self._is_running:
            return
        
        self._is_running = False
        self._stop_event.set()
        
        if self._monitor_thread and self._monitor_thread.is_alive():
            try:
                self._monitor_thread.join(timeout=2)
            except Exception as e:
                logger.debug("Error stopping health monitor thread: %s", e)
        
        logger.info("Health monitor stopped")
    
    def _monitor_loop(self):
        """Main monitoring loop that emits health snapshots."""
        try:
            while not self._stop_event.is_set():
                try:
                    # Generate and emit health snapshot
                    snapshot = health_snapshot()
                    
                    # Log as single JSON line for easy parsing
                    logger.info("[HEALTH] %s", json.dumps(snapshot))
                    
                except Exception as e:
                    logger.error("Error in health snapshot: %s", e, exc_info=True)
                
                # Wait for interval or stop event
                self._stop_event.wait(timeout=self.interval_seconds)
                
        except Exception as e:
            logger.error("Error in health monitor loop: %s", e, exc_info=True)
        finally:
            self._is_running = False


# Global health monitor instance
_health_monitor: Optional[HealthMonitor] = None
_health_monitor_lock = threading.Lock()


def start_health_monitoring(interval_seconds: int = 60):
    """
    Start global health monitoring.
    
    Args:
        interval_seconds: Interval between health snapshots (default: 60)
    """
    global _health_monitor
    
    with _health_monitor_lock:
        if _health_monitor is None:
            _health_monitor = HealthMonitor(interval_seconds=interval_seconds)
        _health_monitor.start()


def stop_health_monitoring():
    """Stop global health monitoring."""
    global _health_monitor
    
    with _health_monitor_lock:
        if _health_monitor:
            _health_monitor.stop()


def get_health_snapshot() -> Dict[str, Any]:
    """
    Get a one-time health snapshot (doesn't require monitoring to be running).
    
    Returns:
        Health snapshot dictionary
    """
    return health_snapshot()

