"""
System detection module - detects hardware and converts to Prolog facts
"""

import os
import platform
import subprocess
import re


def detect_system():
    """
    Detect system hardware and return as a dictionary
    """
    system_info = {
        "os": detect_os(),
        "cpu": detect_cpu(),
        "ram": detect_ram(),
        "gpu": detect_gpu(),
        "disk": detect_disk(),
        "power": detect_power_source(),
    }
    return system_info


def detect_os():
    """Detect operating system"""
    system = platform.system().lower()
    release = platform.release()

    return {
        "type": system,
        "release": release,
        "is_wsl": "microsoft" in platform.release().lower() or "wsl" in platform.release().lower(),
    }


def detect_cpu():
    """Detect CPU information"""
    cpu_info = {
        "cores": os.cpu_count() or 1,
        "physical_cores": os.cpu_count() or 1,
        "brand": "unknown",
        "frequency_mhz": 0,
    }

    system = platform.system().lower()

    if system == "linux":
        try:
            with open("/proc/cpuinfo", "r") as f:
                cpuinfo = f.read()

            # Get brand
            brand_match = re.search(r"model name\s*:\s*(.+)", cpuinfo)
            if brand_match:
                cpu_info["brand"] = brand_match.group(1).strip()

            # Get frequency
            freq_match = re.search(r"cpu MHz\s*:\s*(\d+\.?\d*)", cpuinfo)
            if freq_match:
                cpu_info["frequency_mhz"] = float(freq_match.group(1))

            # Count physical cores (unique core ids)
            core_ids = re.findall(r"core id\s*:\s*(\d+)", cpuinfo)
            if core_ids:
                cpu_info["physical_cores"] = len(set(core_ids))

        except (IOError, OSError):
            pass

    elif system == "windows":
        try:
            result = subprocess.run(
                ["wmic", "cpu", "get", "Name,NumberOfCores,MaxClockSpeed", "/format:csv"],
                capture_output=True, text=True, timeout=5
            )
            lines = [l for l in result.stdout.strip().split("\n") if l.strip()]
            if len(lines) > 1:
                parts = lines[1].split(",")
                if len(parts) >= 4:
                    cpu_info["frequency_mhz"] = float(parts[1]) if parts[1] else 0
                    cpu_info["brand"] = parts[2] if parts[2] else "unknown"
                    cpu_info["physical_cores"] = int(parts[3]) if parts[3] else cpu_info["cores"]
        except (subprocess.TimeoutExpired, subprocess.SubprocessError, ValueError):
            pass

    elif system == "darwin":
        try:
            result = subprocess.run(
                ["sysctl", "-n", "machdep.cpu.brand_string"],
                capture_output=True, text=True, timeout=5
            )
            cpu_info["brand"] = result.stdout.strip()

            result = subprocess.run(
                ["sysctl", "-n", "hw.physicalcpu"],
                capture_output=True, text=True, timeout=5
            )
            cpu_info["physical_cores"] = int(result.stdout.strip())
        except (subprocess.TimeoutExpired, subprocess.SubprocessError, ValueError):
            pass

    # Determine CPU tier
    cores = cpu_info["cores"]
    if cores >= 16:
        cpu_info["tier"] = "high"
    elif cores >= 8:
        cpu_info["tier"] = "medium"
    elif cores >= 4:
        cpu_info["tier"] = "low"
    else:
        cpu_info["tier"] = "minimal"

    return cpu_info


def detect_ram():
    """Detect RAM information"""
    ram_info = {
        "total_gb": 0,
        "available_gb": 0,
        "tier": "unknown",
    }

    system = platform.system().lower()

    if system == "linux":
        try:
            with open("/proc/meminfo", "r") as f:
                meminfo = f.read()

            total_match = re.search(r"MemTotal:\s*(\d+)", meminfo)
            avail_match = re.search(r"MemAvailable:\s*(\d+)", meminfo)

            if total_match:
                ram_info["total_gb"] = round(int(total_match.group(1)) / 1024 / 1024, 1)
            if avail_match:
                ram_info["available_gb"] = round(int(avail_match.group(1)) / 1024 / 1024, 1)

        except (IOError, OSError):
            pass

    elif system == "windows":
        try:
            result = subprocess.run(
                ["wmic", "OS", "get", "TotalVisibleMemorySize,FreePhysicalMemory", "/format:csv"],
                capture_output=True, text=True, timeout=5
            )
            lines = [l for l in result.stdout.strip().split("\n") if l.strip()]
            if len(lines) > 1:
                parts = lines[1].split(",")
                if len(parts) >= 3:
                    ram_info["available_gb"] = round(int(parts[1]) / 1024 / 1024, 1) if parts[1] else 0
                    ram_info["total_gb"] = round(int(parts[2]) / 1024 / 1024, 1) if parts[2] else 0
        except (subprocess.TimeoutExpired, subprocess.SubprocessError, ValueError):
            pass

    elif system == "darwin":
        try:
            result = subprocess.run(
                ["sysctl", "-n", "hw.memsize"],
                capture_output=True, text=True, timeout=5
            )
            ram_info["total_gb"] = round(int(result.stdout.strip()) / 1024 / 1024 / 1024, 1)
        except (subprocess.TimeoutExpired, subprocess.SubprocessError, ValueError):
            pass

    # Determine RAM tier
    total = ram_info["total_gb"]
    if total >= 32:
        ram_info["tier"] = "high"
    elif total >= 16:
        ram_info["tier"] = "medium"
    elif total >= 8:
        ram_info["tier"] = "low"
    else:
        ram_info["tier"] = "minimal"

    return ram_info


def detect_gpu():
    """Detect GPU information"""
    gpu_info = {
        "vendor": "unknown",
        "model": "unknown",
        "vram_gb": 0,
        "has_dedicated": False,
        "tier": "none",
    }

    system = platform.system().lower()

    # Try nvidia-smi first (works on Linux, Windows, and sometimes WSL)
    try:
        result = subprocess.run(
            ["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader,nounits"],
            capture_output=True, text=True, timeout=5
        )
        if result.returncode == 0 and result.stdout.strip():
            parts = result.stdout.strip().split(",")
            gpu_info["vendor"] = "nvidia"
            gpu_info["model"] = parts[0].strip() if parts else "unknown"
            gpu_info["vram_gb"] = round(float(parts[1].strip()) / 1024, 1) if len(parts) > 1 else 0
            gpu_info["has_dedicated"] = True
    except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError):
        pass

    # If nvidia not found, try other methods
    if gpu_info["vendor"] == "unknown":
        if system == "linux":
            try:
                result = subprocess.run(
                    ["lspci"], capture_output=True, text=True, timeout=5
                )
                output = result.stdout.lower()

                if "nvidia" in output:
                    gpu_info["vendor"] = "nvidia"
                    gpu_info["has_dedicated"] = True
                    # Try to extract model
                    match = re.search(r"nvidia.*?\[(.+?)\]", output, re.IGNORECASE)
                    if match:
                        gpu_info["model"] = match.group(1)
                elif "amd" in output or "radeon" in output:
                    gpu_info["vendor"] = "amd"
                    gpu_info["has_dedicated"] = True
                elif "intel" in output:
                    gpu_info["vendor"] = "intel"
                    gpu_info["has_dedicated"] = False

            except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError):
                pass

        elif system == "windows":
            try:
                result = subprocess.run(
                    ["wmic", "path", "win32_videocontroller", "get", "name,adapterram", "/format:csv"],
                    capture_output=True, text=True, timeout=5
                )
                lines = [l for l in result.stdout.strip().split("\n") if l.strip()]
                if len(lines) > 1:
                    parts = lines[1].split(",")
                    if len(parts) >= 3:
                        model = parts[2].lower() if parts[2] else ""
                        if "nvidia" in model:
                            gpu_info["vendor"] = "nvidia"
                            gpu_info["has_dedicated"] = True
                        elif "amd" in model or "radeon" in model:
                            gpu_info["vendor"] = "amd"
                            gpu_info["has_dedicated"] = True
                        elif "intel" in model:
                            gpu_info["vendor"] = "intel"
                        gpu_info["model"] = parts[2] if parts[2] else "unknown"
                        if parts[1]:
                            gpu_info["vram_gb"] = round(int(parts[1]) / 1024 / 1024 / 1024, 1)
            except (subprocess.TimeoutExpired, subprocess.SubprocessError, ValueError):
                pass

    # Determine GPU tier
    vram = gpu_info["vram_gb"]
    if gpu_info["has_dedicated"]:
        if vram >= 12:
            gpu_info["tier"] = "high"
        elif vram >= 6:
            gpu_info["tier"] = "medium"
        elif vram >= 2:
            gpu_info["tier"] = "low"
        else:
            gpu_info["tier"] = "basic"
    else:
        gpu_info["tier"] = "integrated"

    return gpu_info


def detect_disk():
    """Detect disk information"""
    disk_info = {
        "type": "unknown",  # ssd, hdd, nvme
        "total_gb": 0,
        "free_gb": 0,
        "tier": "unknown",
    }

    system = platform.system().lower()

    # Get disk space
    try:
        if system == "windows":
            # Get C: drive info
            result = subprocess.run(
                ["wmic", "logicaldisk", "where", "DeviceID='C:'", "get", "Size,FreeSpace", "/format:csv"],
                capture_output=True, text=True, timeout=5
            )
            lines = [l for l in result.stdout.strip().split("\n") if l.strip()]
            if len(lines) > 1:
                parts = lines[1].split(",")
                if len(parts) >= 3:
                    disk_info["free_gb"] = round(int(parts[1]) / 1024 / 1024 / 1024, 1) if parts[1] else 0
                    disk_info["total_gb"] = round(int(parts[2]) / 1024 / 1024 / 1024, 1) if parts[2] else 0
        else:
            # Linux/Mac - use statvfs
            statvfs = os.statvfs("/")
            disk_info["total_gb"] = round(statvfs.f_frsize * statvfs.f_blocks / 1024 / 1024 / 1024, 1)
            disk_info["free_gb"] = round(statvfs.f_frsize * statvfs.f_bavail / 1024 / 1024 / 1024, 1)
    except (OSError, subprocess.SubprocessError, ValueError):
        pass

    # Try to detect disk type
    if system == "linux":
        try:
            # Check for NVMe
            result = subprocess.run(["ls", "/dev/nvme0"], capture_output=True, timeout=5)
            if result.returncode == 0:
                disk_info["type"] = "nvme"
            else:
                # Check rotational flag for SSD vs HDD
                result = subprocess.run(
                    ["cat", "/sys/block/sda/queue/rotational"],
                    capture_output=True, text=True, timeout=5
                )
                if result.stdout.strip() == "0":
                    disk_info["type"] = "ssd"
                else:
                    disk_info["type"] = "hdd"
        except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError):
            disk_info["type"] = "ssd"  # Assume SSD if we can't detect

    elif system == "windows":
        try:
            result = subprocess.run(
                ["wmic", "diskdrive", "get", "MediaType", "/format:csv"],
                capture_output=True, text=True, timeout=5
            )
            output = result.stdout.lower()
            if "ssd" in output or "solid" in output:
                disk_info["type"] = "ssd"
            elif "nvme" in output:
                disk_info["type"] = "nvme"
            else:
                disk_info["type"] = "hdd"
        except (subprocess.TimeoutExpired, subprocess.SubprocessError):
            disk_info["type"] = "ssd"
    else:
        disk_info["type"] = "ssd"  # Default assumption

    # Determine disk tier based on type and space
    if disk_info["type"] == "nvme":
        disk_info["tier"] = "high"
    elif disk_info["type"] == "ssd":
        disk_info["tier"] = "medium"
    else:
        disk_info["tier"] = "low"

    return disk_info


def detect_power_source():
    """Detect if on battery or AC power"""
    power_info = {
        "on_battery": False,
        "battery_percent": 100,
        "is_laptop": False,
    }

    system = platform.system().lower()

    if system == "linux":
        try:
            # Check for battery
            if os.path.exists("/sys/class/power_supply/BAT0"):
                power_info["is_laptop"] = True

                # Check if on AC
                ac_path = "/sys/class/power_supply/AC/online"
                if os.path.exists(ac_path):
                    with open(ac_path) as f:
                        power_info["on_battery"] = f.read().strip() == "0"

                # Get battery percentage
                cap_path = "/sys/class/power_supply/BAT0/capacity"
                if os.path.exists(cap_path):
                    with open(cap_path) as f:
                        power_info["battery_percent"] = int(f.read().strip())

        except (IOError, OSError, ValueError):
            pass

    elif system == "windows":
        try:
            result = subprocess.run(
                ["wmic", "path", "Win32_Battery", "get", "BatteryStatus,EstimatedChargeRemaining", "/format:csv"],
                capture_output=True, text=True, timeout=5
            )
            lines = [l for l in result.stdout.strip().split("\n") if l.strip()]
            if len(lines) > 1:
                power_info["is_laptop"] = True
                parts = lines[1].split(",")
                if len(parts) >= 3:
                    # BatteryStatus: 1=discharging, 2=AC
                    power_info["on_battery"] = parts[1] == "1" if parts[1] else False
                    power_info["battery_percent"] = int(parts[2]) if parts[2] else 100
        except (subprocess.TimeoutExpired, subprocess.SubprocessError, ValueError):
            pass

    return power_info


def get_system_facts():
    """
    Convert detected system info into Prolog-style facts
    Returns a list of fact tuples: (predicate, *args)
    """
    system = detect_system()
    facts = []

    # OS facts
    os_info = system["os"]
    facts.append(("os_type", os_info["type"]))
    if os_info["is_wsl"]:
        facts.append(("is_wsl", True))

    # CPU facts
    cpu = system["cpu"]
    facts.append(("cpu_cores", cpu["cores"]))
    facts.append(("cpu_physical_cores", cpu["physical_cores"]))
    facts.append(("cpu_tier", cpu["tier"]))
    if cpu["frequency_mhz"] > 0:
        facts.append(("cpu_frequency_mhz", cpu["frequency_mhz"]))

    # RAM facts
    ram = system["ram"]
    facts.append(("ram_total_gb", ram["total_gb"]))
    facts.append(("ram_available_gb", ram["available_gb"]))
    facts.append(("ram_tier", ram["tier"]))

    # GPU facts
    gpu = system["gpu"]
    facts.append(("gpu_vendor", gpu["vendor"]))
    facts.append(("has_dedicated_gpu", gpu["has_dedicated"]))
    facts.append(("gpu_tier", gpu["tier"]))
    if gpu["vram_gb"] > 0:
        facts.append(("gpu_vram_gb", gpu["vram_gb"]))

    # Disk facts
    disk = system["disk"]
    facts.append(("disk_type", disk["type"]))
    facts.append(("disk_total_gb", disk["total_gb"]))
    facts.append(("disk_free_gb", disk["free_gb"]))
    facts.append(("disk_tier", disk["tier"]))

    # Power facts
    power = system["power"]
    facts.append(("is_laptop", power["is_laptop"]))
    facts.append(("on_battery", power["on_battery"]))
    if power["is_laptop"]:
        facts.append(("battery_percent", power["battery_percent"]))

    return facts


def print_system_info():
    """Pretty print system information"""
    system = detect_system()

    print("\n" + "=" * 50)
    print("SYSTEM INFORMATION")
    print("=" * 50)

    # OS
    os_info = system["os"]
    print(f"\nOS: {os_info['type'].title()} {os_info['release']}")
    if os_info["is_wsl"]:
        print("    (Running in WSL)")

    # CPU
    cpu = system["cpu"]
    print(f"\nCPU: {cpu['brand']}")
    print(f"    Cores: {cpu['cores']} ({cpu['physical_cores']} physical)")
    print(f"    Tier: {cpu['tier'].upper()}")

    # RAM
    ram = system["ram"]
    print(f"\nRAM: {ram['total_gb']} GB total ({ram['available_gb']} GB available)")
    print(f"    Tier: {ram['tier'].upper()}")

    # GPU
    gpu = system["gpu"]
    gpu_str = f"{gpu['vendor'].upper()} {gpu['model']}"
    if gpu["vram_gb"] > 0:
        gpu_str += f" ({gpu['vram_gb']} GB VRAM)"
    print(f"\nGPU: {gpu_str}")
    print(f"    Dedicated: {'Yes' if gpu['has_dedicated'] else 'No'}")
    print(f"    Tier: {gpu['tier'].upper()}")

    # Disk
    disk = system["disk"]
    print(f"\nDisk: {disk['type'].upper()} - {disk['total_gb']} GB total ({disk['free_gb']} GB free)")
    print(f"    Tier: {disk['tier'].upper()}")

    # Power
    power = system["power"]
    if power["is_laptop"]:
        status = "Battery" if power["on_battery"] else "AC Power"
        print(f"\nPower: {status} ({power['battery_percent']}%)")
    else:
        print(f"\nPower: Desktop (AC)")

    print("\n" + "=" * 50)


if __name__ == "__main__":
    print_system_info()
    print("\nFacts for Prolog engine:")
    for fact in get_system_facts():
        print(f"  {fact[0]}({', '.join(str(a) for a in fact[1:])})")
