"""
Status Monitor

Monitors system status including container availability, USB devices, and system resources.
"""

import asyncio
import os
import shutil
import subprocess
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

import psutil


class StatusMonitor:
    """Monitors system status and resources"""

    def __init__(self):
        self._status_cache: Dict[str, Any] = {}
        self._monitoring = False

    async def get_system_status(self) -> Dict[str, Any]:
        """Get comprehensive system status"""
        status = {
            "podman": await self._check_podman_status(),
            "vivado": await self._check_vivado_status(),
            "usb_devices": await self._get_usb_device_count(),
            "disk_space": await self._get_disk_space(),
            "root_access": await self._check_root_access(),
            "container_image": await self._check_container_image(),
            "vfio_support": await self._check_vfio_support(),
            "resources": await self._get_resource_usage(),
        }

        self._status_cache = status
        return status

    async def _check_podman_status(self) -> Dict[str, Any]:
        """Check Podman availability and status"""
        try:
            if not shutil.which("podman"):
                return {"status": "not_found", "message": "Podman not found in PATH"}

            # Check if Podman is running
            result = await self._run_command("podman version --format json")
            if result.returncode == 0:
                return {"status": "ready", "message": "Podman available"}
            else:
                return {"status": "error", "message": "Podman not responding"}

        except Exception as e:
            return {"status": "error", "message": f"Podman check failed: {e}"}

    async def _check_vivado_status(self) -> Dict[str, Any]:
        """Check Vivado availability"""
        try:
            # Check for Vivado in common locations
            vivado_paths = [
                "/opt/Xilinx/Vivado",
                "/tools/Xilinx/Vivado",
                shutil.which("vivado"),
            ]

            for path in vivado_paths:
                if path and os.path.exists(path):
                    # Try to get version
                    if os.path.isfile(path):
                        result = await self._run_command(f"{path} -version")
                        if result.returncode == 0:
                            # Extract version from output
                            version = self._extract_vivado_version(result.stdout)
                            return {
                                "status": "detected",
                                "version": version,
                                "path": path,
                            }
                    elif os.path.isdir(path):
                        # Look for version directories
                        versions = [d for d in os.listdir(path) if d.startswith("20")]
                        if versions:
                            latest_version = sorted(versions)[-1]
                            return {
                                "status": "detected",
                                "version": latest_version,
                                "path": path,
                            }

            return {"status": "not_found", "message": "Vivado not detected"}

        except Exception as e:
            return {"status": "error", "message": f"Vivado check failed: {e}"}

    def _extract_vivado_version(self, output: str) -> str:
        """Extract Vivado version from command output"""
        lines = output.split("\n")
        for line in lines:
            if "Vivado" in line and "v" in line:
                # Extract version like "v2022.2"
                parts = line.split()
                for part in parts:
                    if part.startswith("v") and "." in part:
                        return part[1:]  # Remove 'v' prefix
        return "unknown"

    async def _get_usb_device_count(self) -> Dict[str, Any]:
        """Get USB device count"""
        try:
            # Import existing function
            import sys

            sys.path.append(str(Path(__file__).parent.parent.parent.parent))
            from generate import list_usb_devices

            devices = await asyncio.get_event_loop().run_in_executor(
                None, list_usb_devices
            )
            return {"count": len(devices), "devices": devices[:5]}  # Show first 5

        except Exception as e:
            return {"count": 0, "error": str(e)}

    async def _get_disk_space(self) -> Dict[str, Any]:
        """Get disk space information"""
        try:
            disk_usage = psutil.disk_usage("/")
            free_gb = disk_usage.free / (1024**3)
            total_gb = disk_usage.total / (1024**3)
            used_percent = (disk_usage.used / disk_usage.total) * 100

            return {
                "free_gb": round(free_gb, 1),
                "total_gb": round(total_gb, 1),
                "used_percent": round(used_percent, 1),
                "status": "low" if free_gb < 10 else "ok",
            }

        except Exception as e:
            return {"error": str(e)}

    async def _check_root_access(self) -> Dict[str, Any]:
        """Check if running with root privileges"""
        try:
            has_root = os.geteuid() == 0
            return {
                "available": has_root,
                "message": (
                    "Root access available" if has_root else "Root access required"
                ),
            }
        except Exception as e:
            return {"available": False, "error": str(e)}

    async def _check_container_image(self) -> Dict[str, Any]:
        """Check if DMA firmware container image exists"""
        try:
            result = await self._run_command(
                "podman images dma-fw --format '{{.Repository}}:{{.Tag}}'"
            )
            if result.returncode == 0 and "dma-fw" in result.stdout:
                return {"available": True, "image": result.stdout.strip()}
            else:
                return {
                    "available": False,
                    "message": "Container image 'dma-fw' not found",
                }

        except Exception as e:
            return {"available": False, "error": str(e)}

    async def _check_vfio_support(self) -> Dict[str, Any]:
        """Check VFIO support"""
        try:
            vfio_checks = {
                "vfio_module": os.path.exists("/sys/module/vfio"),
                "vfio_pci_driver": os.path.exists("/sys/bus/pci/drivers/vfio-pci"),
                "vfio_device": os.path.exists("/dev/vfio/vfio"),
                "iommu_enabled": self._check_iommu_enabled(),
            }

            all_good = all(vfio_checks.values())
            return {
                "supported": all_good,
                "checks": vfio_checks,
                "message": (
                    "VFIO fully supported" if all_good else "VFIO issues detected"
                ),
            }

        except Exception as e:
            return {"supported": False, "error": str(e)}

    def _check_iommu_enabled(self) -> bool:
        """Check if IOMMU is enabled"""
        try:
            # Check kernel command line for IOMMU parameters
            with open("/proc/cmdline", "r") as f:
                cmdline = f.read()
                return (
                    "iommu=on" in cmdline
                    or "intel_iommu=on" in cmdline
                    or "amd_iommu=on" in cmdline
                )
        except Exception:
            return False

    async def _get_resource_usage(self) -> Dict[str, Any]:
        """Get current resource usage"""
        try:
            cpu_percent = psutil.cpu_percent(interval=0.1)
            memory = psutil.virtual_memory()
            load_avg = os.getloadavg() if hasattr(os, "getloadavg") else (0, 0, 0)

            return {
                "cpu_percent": round(cpu_percent, 1),
                "memory_used_gb": round(memory.used / (1024**3), 1),
                "memory_total_gb": round(memory.total / (1024**3), 1),
                "memory_percent": round(memory.percent, 1),
                "load_average": {
                    "1min": round(load_avg[0], 2),
                    "5min": round(load_avg[1], 2),
                    "15min": round(load_avg[2], 2),
                },
            }

        except Exception as e:
            return {"error": str(e)}

    async def _run_command(self, command: str) -> subprocess.CompletedProcess:
        """Run a command asynchronously"""
        try:
            process = await asyncio.create_subprocess_shell(
                command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
            )
            stdout, stderr = await process.communicate()

            return subprocess.CompletedProcess(
                args=command,
                returncode=process.returncode or 0,
                stdout=stdout.decode(),
                stderr=stderr.decode(),
            )
        except Exception as e:
            return subprocess.CompletedProcess(
                args=command, returncode=1, stdout="", stderr=str(e)
            )

    def get_cached_status(self) -> Dict[str, Any]:
        """Get cached status information"""
        return self._status_cache.copy()

    async def start_monitoring(self, interval: float = 5.0) -> None:
        """Start continuous monitoring"""
        self._monitoring = True
        while self._monitoring:
            await self.get_system_status()
            await asyncio.sleep(interval)

    def stop_monitoring(self) -> None:
        """Stop continuous monitoring"""
        self._monitoring = False

    def is_monitoring(self) -> bool:
        """Check if monitoring is active"""
        return self._monitoring

    def get_status_summary(self) -> Dict[str, str]:
        """Get a summary of system status"""
        status = self._status_cache
        summary = {}

        # Podman status
        podman = status.get("podman", {})
        summary["podman"] = (
            "🐳 Ready" if podman.get("status") == "ready" else "❌ Not Available"
        )

        # Vivado status
        vivado = status.get("vivado", {})
        if vivado.get("status") == "detected":
            summary["vivado"] = f"⚡ {vivado.get('version', 'Unknown')} Detected"
        else:
            summary["vivado"] = "❌ Not Detected"

        # USB devices
        usb = status.get("usb_devices", {})
        count = usb.get("count", 0)
        summary["usb"] = f"🔌 {count} USB Device{'s' if count != 1 else ''} Found"

        # Disk space
        disk = status.get("disk_space", {})
        if "free_gb" in disk:
            free_gb = disk["free_gb"]
            summary["disk"] = f"💾 {free_gb} GB Free"
        else:
            summary["disk"] = "❌ Disk Info Unavailable"

        # Root access
        root = status.get("root_access", {})
        summary["root"] = (
            "🔒 Root Access Available"
            if root.get("available")
            else "❌ Root Access Required"
        )

        return summary
