"""
Device Manager

Manages PCIe device discovery, validation, and enhanced information gathering.
"""

import asyncio
import os
import re
from pathlib import Path
from typing import Any, Dict, List, Optional

from ..models.device import PCIDevice


class DeviceManager:
    """Manages PCIe device discovery and validation."""

    def __init__(self):
        self._device_cache: List[PCIDevice] = []
        self._vendor_db: Dict[str, str] = {}
        self._load_vendor_database()

    def _load_vendor_database(self) -> None:
        """Load PCI vendor database for enhanced device names."""
        # Basic vendor database - in production this could be loaded from pci.ids
        self._vendor_db = {
            "8086": "Intel Corporation",
            "10de": "NVIDIA Corporation",
            "1002": "Advanced Micro Devices",
            "10ec": "Realtek Semiconductor",
            "14e4": "Broadcom",
            "1969": "Qualcomm Atheros",
            "168c": "Qualcomm Atheros",
            "15b3": "Mellanox Technologies",
            "1077": "QLogic Corp.",
            "19a2": "Emulex Corporation",
        }

    async def scan_devices(self) -> List[PCIDevice]:
        """Enhanced device scanning with detailed information."""
        try:
            # Get raw device list from existing generate.py functionality
            raw_devices = await self._get_raw_devices()

            # Enhance each device with additional information
            enhanced_devices = []
            for raw_device in raw_devices:
                try:
                    enhanced = await self._enhance_device_info(raw_device)
                    enhanced_devices.append(enhanced)
                except Exception as e:
                    # Log error but continue with other devices
                    print(
                        f"Warning: Failed to enhance device {raw_device.get('bdf', 'unknown')}: {e}"
                    )
                    continue

            self._device_cache = enhanced_devices
            return enhanced_devices

        except Exception as e:
            raise RuntimeError(f"Failed to scan PCIe devices: {e}")

    async def _get_raw_devices(self) -> List[Dict[str, str]]:
        """Get raw device list using existing generate.py functionality."""
        # Import the existing function
        import sys

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

        # Run in executor to avoid blocking
        loop = asyncio.get_event_loop()
        return await loop.run_in_executor(None, list_pci_devices)

    async def _enhance_device_info(self, raw_device: Dict[str, str]) -> PCIDevice:
        """Enhance raw device information with additional details."""
        bdf = raw_device["bdf"]
        vendor_id = raw_device["ven"]
        device_id = raw_device["dev"]
        device_class = raw_device["class"]

        # Get vendor name from database
        vendor_name = self._vendor_db.get(vendor_id.lower(), f"Vendor {vendor_id}")

        # Extract device name from pretty string
        device_name = self._extract_device_name(raw_device["pretty"])

        # Get additional device information
        driver = await self._get_device_driver(bdf)
        iommu_group = await self._get_iommu_group(bdf)
        power_state = await self._get_power_state(bdf)
        link_speed = await self._get_link_speed(bdf)
        bars = await self._get_device_bars(bdf)

        # Calculate suitability score and compatibility issues
        suitability_score, compatibility_issues = self._assess_device_suitability(
            device_class, driver, bars
        )

        return PCIDevice(
            bdf=bdf,
            vendor_id=vendor_id,
            device_id=device_id,
            vendor_name=vendor_name,
            device_name=device_name,
            device_class=device_class,
            subsystem_vendor="",  # Could be enhanced further
            subsystem_device="",  # Could be enhanced further
            driver=driver,
            iommu_group=iommu_group,
            power_state=power_state,
            link_speed=link_speed,
            bars=bars,
            suitability_score=suitability_score,
            compatibility_issues=compatibility_issues,
        )

    def _extract_device_name(self, pretty_string: str) -> str:
        """Extract device name from lspci pretty string."""
        # Remove BDF and vendor/device IDs to get clean device name
        # Example: "0000:03:00.0 Ethernet controller [0200]: Intel Corporation 82574L Gigabit Network Connection [8086:10d3]"
        match = re.search(r"]: (.+?) \[[\da-fA-F]{4}:[\da-fA-F]{4}\]", pretty_string)
        if match:
            return match.group(1).strip()

        # Fallback: extract everything after the class description
        match = re.search(r"]: (.+)", pretty_string)
        if match:
            return match.group(1).strip()

        return "Unknown Device"

    async def _get_device_driver(self, bdf: str) -> Optional[str]:
        """Get current driver for device."""
        try:
            import sys

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

            loop = asyncio.get_event_loop()
            return await loop.run_in_executor(None, get_current_driver, bdf)
        except Exception:
            return None

    async def _get_iommu_group(self, bdf: str) -> str:
        """Get IOMMU group for device."""
        try:
            import sys

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

            loop = asyncio.get_event_loop()
            return await loop.run_in_executor(None, get_iommu_group, bdf)
        except Exception:
            return "unknown"

    async def _get_power_state(self, bdf: str) -> str:
        """Get device power state."""
        try:
            power_path = f"/sys/bus/pci/devices/{bdf}/power_state"
            if os.path.exists(power_path):
                with open(power_path, "r") as f:
                    return f.read().strip()
        except Exception:
            pass
        return "unknown"

    async def _get_link_speed(self, bdf: str) -> str:
        """Get PCIe link speed."""
        try:
            # Try to get link speed from sysfs
            link_path = f"/sys/bus/pci/devices/{bdf}/current_link_speed"
            if os.path.exists(link_path):
                with open(link_path, "r") as f:
                    return f.read().strip()
        except Exception:
            pass
        return "unknown"

    async def _get_device_bars(self, bdf: str) -> List[Dict[str, Any]]:
        """Get device BAR information."""
        bars = []
        try:
            # Read BAR information from sysfs
            for i in range(6):  # PCIe devices can have up to 6 BARs
                bar_path = f"/sys/bus/pci/devices/{bdf}/resource{i}"
                if os.path.exists(bar_path):
                    with open(bar_path, "r") as f:
                        line = f.read().strip()
                        if (
                            line
                            and line
                            != "0x0000000000000000 0x0000000000000000 0x0000000000000000"
                        ):
                            parts = line.split()
                            if len(parts) >= 3:
                                start = int(parts[0], 16)
                                end = int(parts[1], 16)
                                flags = int(parts[2], 16)
                                size = end - start + 1 if end > start else 0

                                bars.append(
                                    {
                                        "index": i,
                                        "start": start,
                                        "end": end,
                                        "size": size,
                                        "flags": flags,
                                        "type": "memory" if flags & 0x1 == 0 else "io",
                                    }
                                )
        except Exception:
            pass
        return bars

    def _assess_device_suitability(
        self, device_class: str, driver: Optional[str], bars: List[Dict[str, Any]]
    ) -> tuple[float, List[str]]:
        """Assess device suitability for firmware generation."""
        score = 1.0
        issues = []

        # Check device class - network devices are typically good candidates
        if device_class.startswith("02"):  # Network controller
            score += 0.2
        elif device_class.startswith("01"):  # Storage controller
            score += 0.1
        elif device_class.startswith("03"):  # Display controller
            score -= 0.1
            issues.append("Display controllers may have driver conflicts")

        # Check if driver is bound
        if driver and driver != "vfio-pci":
            score -= 0.2
            issues.append(f"Device is bound to {driver} driver")

        # Check BAR configuration
        if not bars:
            score -= 0.3
            issues.append("No memory BARs detected")
        elif len(bars) < 2:
            score -= 0.1
            issues.append("Limited BAR configuration")

        # Ensure score is in valid range
        score = max(0.0, min(1.0, score))

        return score, issues

    def get_cached_devices(self) -> List[PCIDevice]:
        """Get cached device list."""
        return self._device_cache.copy()

    async def refresh_devices(self) -> List[PCIDevice]:
        """Refresh device list."""
        return await self.scan_devices()

    def find_device_by_bdf(self, bdf: str) -> Optional[PCIDevice]:
        """Find device by BDF address."""
        for device in self._device_cache:
            if device.bdf == bdf:
                return device
        return None

    def get_suitable_devices(self) -> List[PCIDevice]:
        """Get list of devices suitable for firmware generation."""
        return [device for device in self._device_cache if device.is_suitable]
