import os
import sys
import subprocess
import platform
import time

import psutil
import json
from typing import List, Dict, Tuple, Optional, Any


class OSResources:
    class HostCpu:
        def __init__(self):
            self.cores = psutil.cpu_count(logical=False) or 1
            self.logical_cores = psutil.cpu_count(logical=True) or 1
            self.vendor = self._get_cpu_vendor()
            self.model = self._get_cpu_model()
            self.architecture = platform.machine()
            self.usage = 0.0
            self.frequency = self._get_frequency()

        def _get_cpu_vendor(self) -> str:
            try:
                if platform.system() == "Windows":
                    return platform.processor()
                elif platform.system() == "Darwin":
                    return subprocess.check_output(['sysctl', '-n', 'machdep.cpu.vendor']).decode().strip()
                else:
                    with open('/proc/cpuinfo') as f:
                        for line in f:
                            if 'vendor_id' in line.lower():
                                return line.split(':')[1].strip()
            except:
                pass
            return "Unknown"

        def _get_cpu_model(self) -> str:
            try:
                if platform.system() == "Windows":
                    return platform.processor()
                elif platform.system() == "Darwin":
                    return subprocess.check_output(['sysctl', '-n', 'machdep.cpu.brand_string']).decode().strip()
                else:
                    with open('/proc/cpuinfo') as f:
                        for line in f:
                            if 'model name' in line.lower():
                                return line.split(':')[1].strip()
            except:
                pass
            return "Unknown CPU"

        def _get_frequency(self) -> float:
            try:
                return psutil.cpu_freq().current
            except:
                return 0.0

        def update_usage(self):
            self.usage = psutil.cpu_percent(interval=0.1)

    class HostGpu:
        def __init__(self, name: str, memory_total: int = 0, driver: str = ""):
            self.name = name
            self.memory_total = memory_total
            self.driver = driver
            self.memory_used = 0

    class HostMemory:
        def __init__(self):
            self.total = psutil.virtual_memory().total
            self.available = psutil.virtual_memory().available
            self.used = psutil.virtual_memory().used
            self.percent = psutil.virtual_memory().percent

    class HostDisk:
        def __init__(self, device: str, mountpoint: str, fstype: str, total: int, used: int):
            self.device = device
            self.mountpoint = mountpoint
            self.fstype = fstype
            self.total = total
            self.used = used
            self.percent = (used / total) * 100 if total > 0 else 0

    class HostNetwork:
        def __init__(self, name: str, ip: str, mac: str, speed: int = 0):
            self.name = name
            self.ip = ip
            self.mac = mac
            self.speed = speed
            self.bytes_sent = 0
            self.bytes_recv = 0

    def __init__(self, core):
        self.core = core
        self.cpu = self.HostCpu()
        self._network_stats = {}

    def get_detailed_cpu_info(self) -> Dict[str, Any]:
        """Возвращает детальную информацию о CPU"""
        return {
            "physical_cores": self.cpu.cores,
            "logical_cores": self.cpu.logical_cores,
            "vendor": self.cpu.vendor,
            "model": self.cpu.model,
            "architecture": self.cpu.architecture,
            "frequency_mhz": self.cpu.frequency,
            "usage_percent": self.cpu.usage
        }

    def get_gpu_list(self) -> List[HostGpu]:
        """Расширенное обнаружение GPU с поддержкой всех платформ"""
        gpus = []
        try:
            system = platform.system()

            if system == "Windows":
                gpus.extend(self._get_windows_gpus())
            elif system == "Darwin":
                gpus.extend(self._get_macos_gpus())
            else:  # Linux/Android/FreeBSD/OpenWrt
                gpus.extend(self._get_linux_gpus())

        except Exception as e:
            print(f"GPU detection error: {e}", file=sys.stderr)

        return gpus if gpus else [self.HostGpu("Generic GPU")]

    def _get_windows_gpus(self) -> List[HostGpu]:
        gpus = []
        try:
            import wmi
            w = wmi.WMI()
            for gpu in w.Win32_VideoController():
                gpus.append(self.HostGpu(
                    name=gpu.Name or "Unknown GPU",
                    memory_total=gpu.AdapterRAM if gpu.AdapterRAM else 0
                ))
        except:
            # Fallback для систем без WMI
            try:
                result = subprocess.run([
                    'powershell',
                    'Get-WmiObject Win32_VideoController | Select-Object Name, AdapterRAM | ConvertTo-Json'
                ], capture_output=True, text=True, timeout=10)
                if result.returncode == 0:
                    gpu_info = json.loads(result.stdout)
                    gpus.append(self.HostGpu(
                        name=gpu_info.get('Name', 'Unknown GPU'),
                        memory_total=gpu_info.get('AdapterRAM', 0)
                    ))
            except:
                pass
        return gpus

    def _get_macos_gpus(self) -> List[HostGpu]:
        gpus = []
        try:
            # Основная информация через system_profiler
            result = subprocess.run([
                'system_profiler', 'SPDisplaysDataType', '-json'
            ], capture_output=True, text=True, timeout=10)

            if result.returncode == 0:
                data = json.loads(result.stdout)
                for gpu in data.get('SPDisplaysDataType', []):
                    gpus.append(self.HostGpu(
                        name=gpu.get('sppci_model', 'Unknown GPU'),
                        memory_total=int(gpu.get('spdisplays_vram', 0))
                    ))
        except:
            # Упрощенный fallback
            try:
                result = subprocess.run([
                    'system_profiler', 'SPDisplaysDataType'
                ], capture_output=True, text=True, timeout=10)
                if 'Chipset Model' in result.stdout:
                    gpus.append(self.HostGpu("Apple GPU"))
            except:
                pass
        return gpus

    def _get_linux_gpus(self) -> List[HostGpu]:
        gpus = []
        try:
            # Попытка через lspci
            result = subprocess.run([
                'lspci', '-mm', '-v'
            ], capture_output=True, text=True, timeout=5)

            if result.returncode == 0:
                for line in result.stdout.split('\n'):
                    if 'VGA' in line or '3D' in line:
                        parts = line.split('"')
                        if len(parts) > 3:
                            gpus.append(self.HostGpu(parts[3]))
        except:
            pass

        # Дополнительные проверки для разных платформ
        if not gpus:
            try:
                # Проверка для Android/Termux
                if os.path.exists('/system/bin/getprop'):
                    result = subprocess.run([
                        'getprop', 'ro.hardware.gpu'
                    ], capture_output=True, text=True)
                    if result.returncode == 0:
                        gpus.append(self.HostGpu(result.stdout.strip()))
            except:
                pass

        return gpus

    def get_memory_info(self) -> HostMemory:
        """Возвращает детальную информацию о памяти"""
        return self.HostMemory()

    def get_detailed_storage_info(self) -> List[HostDisk]:
        """Возвращает детальную информацию о дисках"""
        disks = []
        try:
            for partition in psutil.disk_partitions():
                try:
                    usage = psutil.disk_usage(partition.mountpoint)
                    disks.append(self.HostDisk(
                        device=partition.device,
                        mountpoint=partition.mountpoint,
                        fstype=partition.fstype,
                        total=usage.total,
                        used=usage.used
                    ))
                except PermissionError:
                    continue
        except:
            pass
        return disks

    def get_network_info(self) -> List[HostNetwork]:
        """Возвращает детальную информацию о сетевых интерфейсах"""
        interfaces = []
        try:
            # Получаем статистику и адреса интерфейсов
            stats = psutil.net_if_stats()
            addrs = psutil.net_if_addrs()

            for name, addresses in addrs.items():
                ip = ""
                mac = ""
                speed = 0

                # Обрабатываем все адреса для данного интерфейса
                for addr in addresses:
                    try:
                        if addr.family == psutil.AF_LINK:  # MAC-адрес
                            mac = addr.address
                        elif addr.family in [psutil.AF_INET, psutil.AF_INET6]:  # IPv4/IPv6
                            # Предпочитаем IPv4, но используем IPv6 если IPv4 нет
                            if not ip or addr.family == psutil.AF_INET:
                                ip = addr.address
                    except:
                        continue

                # Получаем скорость интерфейса
                try:
                    if name in stats:
                        speed = stats[name].speed
                        # Если скорость отрицательная или неопределенная, устанавливаем 0
                        if speed < 0:
                            speed = 0
                except:
                    speed = 0

                # Пропускаем loopback и неработающие интерфейсы
                try:
                    if name == "lo" or (name in stats and not stats[name].isup):
                        continue
                except:
                    pass

                # Создаем объект сетевого интерфейса
                interfaces.append(self.HostNetwork(name, ip, mac, speed))

        except Exception as e:
            print(f"Network info error: {e}", file=sys.stderr)
            # Fallback: пытаемся получить хотя бы базовую информацию
            try:
                fallback_addrs = psutil.net_if_addrs()
                for name in fallback_addrs.keys():
                    if name != "lo":  # Пропускаем loopback
                        interfaces.append(self.HostNetwork(name, "", "", 0))
            except:
                pass

        return interfaces

    # Также добавим упрощенную версию для обратной совместимости
    def get_network_adapters(self) -> List[str]:
        """Упрощенный метод для получения списка сетевых адаптеров (обратная совместимость)"""
        try:
            return list(psutil.net_if_addrs().keys())
        except:
            return []

    # Добавим метод для получения текущей сетевой статистики
    def get_network_stats(self) -> Dict[str, Dict]:
        """Возвращает статистику по сетевым интерфейсам"""
        stats = {}
        try:
            io_counters = psutil.net_io_counters(pernic=True)
            for name, counters in io_counters.items():
                stats[name] = {
                    'bytes_sent': counters.bytes_sent,
                    'bytes_recv': counters.bytes_recv,
                    'packets_sent': counters.packets_sent,
                    'packets_recv': counters.packets_recv,
                    'errors_in': counters.errin,
                    'errors_out': counters.errout,
                    'drops_in': counters.dropin,
                    'drops_out': counters.dropout
                }
        except Exception as e:
            print(f"Network stats error: {e}", file=sys.stderr)

        return stats

    # Добавим метод для получения информации о подключениях
    def get_network_connections(self) -> List[Dict]:
        """Возвращает информацию о сетевых подключениях"""
        connections = []
        try:
            for conn in psutil.net_connections(kind='inet'):
                try:
                    conn_info = {
                        'fd': conn.fd,
                        'family': conn.family.name,
                        'type': conn.type.name,
                        'laddr': f"{conn.laddr.ip}:{conn.laddr.port}" if conn.laddr else "",
                        'raddr': f"{conn.raddr.ip}:{conn.raddr.port}" if conn.raddr else "",
                        'status': conn.status,
                        'pid': conn.pid
                    }
                    connections.append(conn_info)
                except:
                    continue
        except Exception as e:
            print(f"Network connections error: {e}", file=sys.stderr)

        return connections

    # Добавим метод для проверки интернет-соединения
    def check_internet_connection(self, timeout: int = 5) -> Dict[str, Any]:
        """Проверяет доступность интернета"""
        import urllib.request
        import socket

        test_hosts = [
            "8.8.8.8",  # Google DNS
            "1.1.1.1",  # Cloudflare DNS
            "208.67.222.222"  # OpenDNS
        ]

        result = {
            'connected': False,
            'latency': None,
            'method': 'icmp',
            'details': ''
        }

        # Сначала проверяем ICMP ping
        for host in test_hosts:
            try:
                socket.setdefaulttimeout(timeout)
                start_time = time.time()
                subprocess.run(
                    ['ping', '-c', '1', '-W', str(timeout), host] if platform.system() != "Windows" else
                    ['ping', '-n', '1', '-w', str(timeout * 1000), host],
                    capture_output=True,
                    timeout=timeout + 1
                )
                result['latency'] = (time.time() - start_time) * 1000
                result['connected'] = True
                result['details'] = f"Ping successful to {host}"
                break
            except:
                continue

        # Если ping не работает, пробуем HTTP запрос
        if not result['connected']:
            test_urls = [
                "http://www.google.com",
                "http://www.cloudflare.com",
                "http://www.apple.com"
            ]

            for url in test_urls:
                try:
                    start_time = time.time()
                    urllib.request.urlopen(url, timeout=timeout)
                    result['latency'] = (time.time() - start_time) * 1000
                    result['connected'] = True
                    result['method'] = 'http'
                    result['details'] = f"HTTP request successful to {url}"
                    break
                except:
                    continue

        return result

    def get_system_uptime(self) -> float:
        """Возвращает uptime системы в секундах"""
        try:
            return psutil.boot_time()
        except:
            return 0

    def get_running_processes(self) -> List[Dict]:
        """Возвращает список запущенных процессов"""
        processes = []
        try:
            for proc in psutil.process_iter(['pid', 'name', 'memory_percent', 'cpu_percent']):
                processes.append(proc.info)
        except:
            pass
        return processes

    def get_temperature_info(self) -> Dict[str, float]:
        """Возвращает информацию о температуре компонентов"""
        temps = {}
        try:
            if hasattr(psutil, "sensors_temperatures"):
                temps = psutil.sensors_temperatures()
            else:
                # Ручное чтение для Linux
                if platform.system() == "Linux":
                    thermal_path = "/sys/class/thermal"
                    if os.path.exists(thermal_path):
                        for zone in os.listdir(thermal_path):
                            if zone.startswith("thermal_zone"):
                                try:
                                    with open(f"{thermal_path}/{zone}/type") as f:
                                        name = f.read().strip()
                                    with open(f"{thermal_path}/{zone}/temp") as f:
                                        temp = float(f.read()) / 1000.0
                                        temps[name] = temp
                                except:
                                    continue
        except:
            pass
        return temps

    def get_battery_info(self) -> Optional[Dict]:
        """Возвращает информацию о батарее"""
        try:
            battery = psutil.sensors_battery()
            if battery:
                return {
                    "percent": battery.percent,
                    "power_plugged": battery.power_plugged,
                    "time_left": battery.secsleft
                }
        except:
            pass
        return None

    def get_os_version(self) -> Dict[str, str]:
        """Возвращает детальную информацию о версии ОС"""
        return {
            "system": platform.system(),
            "release": platform.release(),
            "version": platform.version(),
            "architecture": platform.architecture()[0],
            "platform": platform.platform()
        }

    def get_load_average(self) -> Tuple[float, float, float]:
        """Возвращает среднюю нагрузку за 1, 5 и 15 минут"""
        try:
            return os.getloadavg()
        except:
            return (0, 0, 0)

    def update_dynamic_stats(self):
        """Обновляет динамическую статистику (вызывать периодически)"""
        self.cpu.update_usage()

        # Обновление сетевой статистики
        try:
            current_stats = psutil.net_io_counters(pernic=True)
            for name, stats in current_stats.items():
                if name in self._network_stats:
                    prev = self._network_stats[name]
                    self._network_stats[name] = {
                        'bytes_sent': stats.bytes_sent - prev.bytes_sent,
                        'bytes_recv': stats.bytes_recv - prev.bytes_recv
                    }
            self._network_stats = current_stats
        except:
            pass

    def get_system_warnings(self) -> List[str]:
        """Возвращает предупреждения о состоянии системы"""
        warnings = []
        try:
            # Проверка памяти
            mem = self.get_memory_info()
            if mem.percent > 90:
                warnings.append("High memory usage")

            # Проверка дисков
            for disk in self.get_detailed_storage_info():
                if disk.percent > 95:
                    warnings.append(f"Disk {disk.mountpoint} almost full")

            # Проверка температуры
            temps = self.get_temperature_info()
            for name, temp in temps.items():
                if temp > 80:  # градусов
                    warnings.append(f"High temperature: {name}")

        except:
            pass

        return warnings


# Пример использования
if __name__ == "__main__":
    resources = OSResources(None)

    print("CPU Info:", resources.get_detailed_cpu_info())
    print("GPU List:", [gpu.name for gpu in resources.get_gpu_list()])
    print("Memory:", resources.get_memory_info().__dict__)
    print("Storage:", [disk.__dict__ for disk in resources.get_detailed_storage_info()])
    print("Network:", [net.__dict__ for net in resources.get_network_info()])
    print("OS Version:", resources.get_os_version())
    print("Uptime:", resources.get_system_uptime())
    print("Warnings:", resources.get_system_warnings())