from dataclasses import dataclass
import os
from pathlib import Path
import platform
import subprocess

import cpuinfo
import nonebot
from nonebot.utils import run_sync
import psutil
from pydantic import BaseModel

from zhenxun.configs.config import BotConfig
from zhenxun.services.log import logger
from zhenxun.utils.http_utils import AsyncHttpx

BAIDU_URL = "https://www.baidu.com/"
GOOGLE_URL = "https://www.google.com/"

VERSION_FILE = Path() / "__version__"
ARM_KEY = "aarch64"


@dataclass
class CPUInfo:
    core: int
    """CPU 物理核心数"""
    usage: float
    """CPU 占用百分比，取值范围(0,100]"""
    freq: float
    """CPU 的时钟速度（单位：GHz）"""

    @classmethod
    def get_cpu_info(cls):
        cpu_core = psutil.cpu_count(logical=False)
        cpu_usage = psutil.cpu_percent(interval=0.1)
        if _cpu_freq := psutil.cpu_freq():
            cpu_freq = round(_cpu_freq.current / 1000, 2)
        else:
            cpu_freq = 0
        return CPUInfo(core=cpu_core, usage=cpu_usage, freq=cpu_freq)


@dataclass
class RAMInfo:
    """RAM 信息（单位：GB）"""

    total: float
    """RAM 总量"""
    usage: float
    """当前 RAM 占用量/GB"""

    @classmethod
    def get_ram_info(cls):
        ram_total = round(psutil.virtual_memory().total / (1024**3), 2)
        ram_usage = round(psutil.virtual_memory().used / (1024**3), 2)

        return RAMInfo(total=ram_total, usage=ram_usage)


@dataclass
class SwapMemory:
    """Swap 信息（单位：GB）"""

    total: float
    """Swap 总量"""
    usage: float
    """当前 Swap 占用量/GB"""

    @classmethod
    def get_swap_info(cls):
        swap_total = round(psutil.swap_memory().total / (1024**3), 2)
        swap_usage = round(psutil.swap_memory().used / (1024**3), 2)

        return SwapMemory(total=swap_total, usage=swap_usage)


@dataclass
class DiskInfo:
    """硬盘信息"""

    total: float
    """硬盘总量"""
    usage: float
    """当前硬盘占用量/GB"""

    @classmethod
    def get_disk_info(cls):
        disk_total = round(psutil.disk_usage("/").total / (1024**3), 2)
        disk_usage = round(psutil.disk_usage("/").used / (1024**3), 2)

        return DiskInfo(total=disk_total, usage=disk_usage)


class SystemInfo(BaseModel):
    """系统信息"""

    cpu: CPUInfo
    """CPU信息"""
    ram: RAMInfo
    """RAM信息"""
    swap: SwapMemory
    """SWAP信息"""
    disk: DiskInfo
    """DISK信息"""

    def get_system_info(self):
        return {
            "cpu_info": f"{self.cpu.usage}% - {self.cpu.freq}Ghz "
            f"[{self.cpu.core} core]",
            "cpu_process": self.cpu.usage,
            "ram_info": f"{self.ram.usage} / {self.ram.total} GB",
            "ram_process": (
                0 if self.ram.total == 0 else (self.ram.usage / self.ram.total * 100)
            ),
            "swap_info": f"{self.swap.usage} / {self.swap.total} GB",
            "swap_process": (
                0 if self.swap.total == 0 else (self.swap.usage / self.swap.total * 100)
            ),
            "disk_info": f"{self.disk.usage} / {self.disk.total} GB",
            "disk_process": (
                0 if self.disk.total == 0 else (self.disk.usage / self.disk.total * 100)
            ),
        }


@run_sync
def __build_status() -> SystemInfo:
    """获取 `CPU` `RAM` `SWAP` `DISK` 信息"""
    cpu = CPUInfo.get_cpu_info()
    ram = RAMInfo.get_ram_info()
    swap = SwapMemory.get_swap_info()
    disk = DiskInfo.get_disk_info()

    return SystemInfo(cpu=cpu, ram=ram, swap=swap, disk=disk)


async def __get_network_info():
    """网络请求"""
    baidu, google = True, True
    try:
        await AsyncHttpx.get(BAIDU_URL, timeout=5)
    except Exception as e:
        logger.warning("自检：百度无法访问...", e=e)
        baidu = False
    try:
        await AsyncHttpx.get(GOOGLE_URL, timeout=5)
    except Exception as e:
        logger.warning("自检：谷歌无法访问...", e=e)
        google = False
    return baidu, google


def __get_version() -> str | None:
    """获取版本信息"""
    if VERSION_FILE.exists():
        with open(VERSION_FILE, encoding="utf-8") as f:
            if text := f.read():
                return text.split(":")[-1]
    return None


def __get_arm_cpu():
    env = os.environ.copy()
    env["LC_ALL"] = "en_US.UTF-8"
    cpu_info = subprocess.check_output(["lscpu"], env=env).decode()
    model_name = ""
    cpu_freq = 0
    for line in cpu_info.splitlines():
        if "Model name" in line:
            model_name = line.split(":")[1].strip()
        if "CPU MHz" in line:
            cpu_freq = float(line.split(":")[1].strip())
    return model_name, cpu_freq


def __get_arm_oracle_cpu_freq():
    cpu_freq = subprocess.check_output(
        ["dmidecode", "-s", "processor-frequency"]
    ).decode()
    return round(float(cpu_freq.split()[0]) / 1000, 2)


async def get_status_info() -> dict:
    """获取信息"""
    data = await __build_status()

    system = platform.uname()
    if system.machine == ARM_KEY and not (
        cpuinfo.get_cpu_info().get("brand_raw") and data.cpu.freq
    ):
        model_name, cpu_freq = __get_arm_cpu()
        if not data.cpu.freq:
            data.cpu.freq = cpu_freq or __get_arm_oracle_cpu_freq()
        data = data.get_system_info()
        data["brand_raw"] = model_name
    else:
        data = data.get_system_info()
        data["brand_raw"] = cpuinfo.get_cpu_info().get("brand_raw", "Unknown")

    baidu, google = await __get_network_info()
    data["baidu"] = "#8CC265" if baidu else "red"
    data["google"] = "#8CC265" if google else "red"

    data["system"] = f"{system.system} {system.release}"
    data["version"] = __get_version()
    data["plugin_count"] = len(nonebot.get_loaded_plugins())
    data["nickname"] = BotConfig.self_nickname
    return data
