"""Telemetry Collection - Gathers system metrics safely."""

import logging
import time
import psutil
from typing import Dict, List, Any, Optional
from doctor.core.os_detect import detect_os, is_windows, is_macos, is_linux
from doctor.core.command_runner import run_command, CommandExecutionError


logger = logging.getLogger(__name__)


def collect_cpu_usage() -> Dict[str, Any]:
    """
    Collect CPU usage metrics.
    
    Returns:
        Dictionary with CPU metrics.
    """
    try:
        # Get CPU usage over 1 second intervals (3 samples)
        cpu_percentages = []
        for _ in range(3):
            cpu_percentages.append(psutil.cpu_percent(interval=1))
        
        avg_cpu = sum(cpu_percentages) / len(cpu_percentages)
        max_cpu = max(cpu_percentages)
        
        return {
            "average": round(avg_cpu, 2),
            "max": round(max_cpu, 2),
            "samples": cpu_percentages,
            "count": psutil.cpu_count(logical=True)
        }
    except Exception as e:
        logger.warning(f"Failed to collect CPU usage: {e}")
        return {"average": 0, "max": 0, "samples": [], "count": 0}


def collect_memory_usage() -> Dict[str, Any]:
    """
    Collect memory usage metrics.
    
    Returns:
        Dictionary with memory metrics.
    """
    try:
        mem = psutil.virtual_memory()
        return {
            "total_gb": round(mem.total / (1024**3), 2),
            "available_gb": round(mem.available / (1024**3), 2),
            "used_gb": round(mem.used / (1024**3), 2),
            "percent": round(mem.percent, 2)
        }
    except Exception as e:
        logger.warning(f"Failed to collect memory usage: {e}")
        return {"total_gb": 0, "available_gb": 0, "used_gb": 0, "percent": 0}


def collect_disk_usage() -> List[Dict[str, Any]]:
    """
    Collect disk usage metrics for all partitions.
    
    Returns:
        List of dictionaries with disk metrics per partition.
    """
    disks = []
    try:
        for partition in psutil.disk_partitions():
            try:
                usage = psutil.disk_usage(partition.mountpoint)
                disks.append({
                    "device": partition.device,
                    "mountpoint": partition.mountpoint,
                    "fstype": partition.fstype,
                    "total_gb": round(usage.total / (1024**3), 2),
                    "used_gb": round(usage.used / (1024**3), 2),
                    "free_gb": round(usage.free / (1024**3), 2),
                    "percent": round(usage.percent, 2)
                })
            except PermissionError:
                logger.debug(f"Permission denied for {partition.mountpoint}")
                continue
    except Exception as e:
        logger.warning(f"Failed to collect disk usage: {e}")
    
    return disks


def collect_top_processes(limit: int = 10) -> List[Dict[str, Any]]:
    """
    Collect top CPU-consuming processes with enhanced details.
    
    Args:
        limit: Maximum number of processes to return.
        
    Returns:
        List of process dictionaries with memory in MB and process type.
    """
    processes = []
    try:
        mem_total_gb = psutil.virtual_memory().total / (1024**3)
        
        for proc in psutil.process_iter(['pid', 'name', 'memory_percent']):
            try:
                pinfo = proc.info
                memory_percent = pinfo.get('memory_percent') or 0
                
                # Skip processes with no memory usage
                if memory_percent == 0 or memory_percent is None:
                    continue
                
                memory_mb = (memory_percent / 100) * mem_total_gb * 1024
                
                # Get CPU percent (requires calling the method with interval)
                try:
                    cpu_percent = proc.cpu_percent(interval=0.1)
                except (psutil.NoSuchProcess, psutil.AccessDenied, AttributeError):
                    cpu_percent = 0
                
                # Determine process type (simplified heuristic)
                process_name = pinfo['name'].lower()
                process_type = "user"
                if any(x in process_name for x in ['chrome', 'firefox', 'safari', 'edge', 'brave', 'slack', 'discord', 'spotify', 'zoom', 'teams']):
                    process_type = "user"
                elif any(x in process_name for x in ['docker', 'kubectl', 'node', 'python', 'java', 'code', 'xcode', 'android', 'studio']):
                    process_type = "development"
                elif any(x in process_name for x in ['kernel', 'system', 'launchd', 'WindowServer', 'com.apple', 'kext']):
                    process_type = "system"
                
                processes.append({
                    "pid": pinfo['pid'],
                    "name": pinfo['name'],
                    "cpu_percent": round(cpu_percent, 2),
                    "memory_percent": round(memory_percent, 2),
                    "memory_mb": round(memory_mb, 0),
                    "type": process_type
                })
            except (psutil.NoSuchProcess, psutil.AccessDenied, AttributeError, KeyError):
                continue
        
        # Return top processes by memory (most relevant for resource pressure)
        processes.sort(key=lambda x: x['memory_percent'], reverse=True)
        return processes[:limit]
    except Exception as e:
        logger.warning(f"Failed to collect top processes: {e}")
        return []


def get_top_resource_contributors(telemetry: Dict[str, Any]) -> Dict[str, Any]:
    """
    Extract top memory and CPU contributors from telemetry.
    
    Args:
        telemetry: Complete telemetry data.
        
    Returns:
        Dictionary with top_memory_processes and top_cpu_processes.
    """
    top_processes = telemetry.get("top_processes", [])
    memory_total_gb = telemetry.get("memory", {}).get("total_gb", 0)
    
    # Get top memory consumers (top 5)
    memory_processes = sorted(
        [p for p in top_processes if p.get("memory_percent", 0) > 0],
        key=lambda x: x.get("memory_percent", 0),
        reverse=True
    )[:5]
    
    # Format memory processes
    top_memory = []
    for proc in memory_processes:
        # Calculate memory in MB if not already present
        memory_percent = proc.get("memory_percent", 0)
        memory_mb = proc.get("memory_mb", 0)
        if memory_mb == 0 and memory_total_gb > 0:
            memory_mb = (memory_percent / 100) * memory_total_gb * 1024
        
        memory_gb = memory_mb / 1024 if memory_mb >= 1024 else None
        
        top_memory.append({
            "name": proc.get("name", "Unknown"),
            "memory_mb": int(memory_mb),
            "memory_gb": round(memory_gb, 2) if memory_gb else None,
            "percent": round(memory_percent, 1),
            "type": proc.get("type", "user")
        })
    
    # Get top CPU consumers (top 5)
    cpu_processes = sorted(
        [p for p in top_processes if p.get("cpu_percent", 0) > 0],
        key=lambda x: x.get("cpu_percent", 0),
        reverse=True
    )[:5]
    
    # Format CPU processes
    top_cpu = []
    for proc in cpu_processes:
        top_cpu.append({
            "name": proc.get("name", "Unknown"),
            "cpu_percent": round(proc.get("cpu_percent", 0), 1),
            "type": proc.get("type", "user")
        })
    
    return {
        "top_memory_processes": top_memory,
        "top_cpu_processes": top_cpu
    }


def collect_uptime() -> Dict[str, Any]:
    """
    Collect system uptime.
    
    Returns:
        Dictionary with uptime information.
    """
    try:
        boot_time = psutil.boot_time()
        uptime_seconds = time.time() - boot_time
        uptime_hours = uptime_seconds / 3600
        uptime_days = uptime_hours / 24
        
        return {
            "seconds": round(uptime_seconds, 0),
            "hours": round(uptime_hours, 2),
            "days": round(uptime_days, 2),
            "boot_time": boot_time
        }
    except Exception as e:
        logger.warning(f"Failed to collect uptime: {e}")
        return {"seconds": 0, "hours": 0, "days": 0, "boot_time": 0}


def collect_battery_info() -> Optional[Dict[str, Any]]:
    """
    Collect battery information if available.
    
    Returns:
        Dictionary with battery metrics or None if not available.
    """
    try:
        battery = psutil.sensors_battery()
        if battery is None:
            return None
        
        return {
            "percent": round(battery.percent, 2),
            "plugged": battery.power_plugged,
            "seconds_left": battery.secsleft if battery.secsleft != psutil.POWER_TIME_UNLIMITED else None
        }
    except Exception as e:
        logger.debug(f"Battery info not available: {e}")
        return None


def collect_all_telemetry() -> Dict[str, Any]:
    """
    Collect all available telemetry data.
    
    Returns:
        Dictionary containing all collected metrics.
    """
    logger.info("Starting telemetry collection...")
    
    # Import extended telemetry here to avoid circular imports
    from doctor.core.extended_telemetry import collect_all_extended_telemetry
    
    telemetry = {
        "os": detect_os(),
        "cpu": collect_cpu_usage(),
        "memory": collect_memory_usage(),
        "disk": collect_disk_usage(),
        "top_processes": collect_top_processes(),
        "uptime": collect_uptime(),
        "battery": collect_battery_info(),
        "timestamp": time.time(),
        "extended_telemetry": collect_all_extended_telemetry()
    }
    
    logger.info("Telemetry collection completed")
    return telemetry
