#!/usr/bin/env python3
import subprocess
import re
import time
import os
import sys
from typing import Dict, List, Optional
import tkinter as tk  


def run_command(cmd: str) -> str:
    """执行shell命令并返回输出（完全静默，不弹出窗口）"""
    try:
        process = subprocess.Popen(
            cmd,
            shell=True,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
        )
        stdout, stderr = process.communicate()
        return stdout.strip() if process.returncode == 0 else ""
    except Exception:
        return ""


def get_cpu_temp() -> Dict[str, float]:
    """获取CPU核心/整体温度"""
    output = run_command("sensors")
    if not output:
        return {}
    
    temp_patterns = [
        r"Package id 0:\s*[\+\-]?([\d.]+)°C",
        r"Core \d+:\s+([\d.]+)°C",
        r"Tdie:\s+([\d.]+)°C",
        r"Tctl:\s+([\d.]+)°C",
        r"CPU Temperature:\s+([\d.]+)°C",
        r"CPU Temp:\s+([\d.]+)°C",
    ]
    
    max_temp = 0.0
    for pattern in temp_patterns:
        match = re.search(pattern, output, re.IGNORECASE)
        if match:
            temp = float(match.group(1))
            max_temp = max(max_temp, temp)
    
    return {"cpu": round(max_temp, 1)} if max_temp > 0 else {}


def get_battery_percentage() -> Optional[float]:
    """获取笔记本电池电量百分比（优先用upower，失败则试sysfs，均失败返回None）"""
    battery_dev = run_command("upower -e | grep -i battery")
    if battery_dev:
        output = run_command(f"upower -i {battery_dev.strip()}")
        match = re.search(r"percentage:\s+([\d.]+)\s*%?", output, re.IGNORECASE)
        if match:
            return round(float(match.group(1)), 1)
    
    sysfs_paths = [
        "/sys/class/power_supply/BAT0/capacity",
        "/sys/class/power_supply/BAT1/capacity",
    ]
    for path in sysfs_paths:
        if os.path.exists(path):
            try:
                with open(path, 'r', encoding='utf-8') as f:
                    content = f.read().strip()
                    return round(float(content), 1) if content else None
            except Exception:
                continue
    
    return None


def get_fan_speeds() -> List[float]:
    """获取风扇转速"""
    output = run_command("sensors")
    if not output:
        return []
    
    fan_pattern = r"(fan|sysfan|cpu_fan|pwmfan)\d+:\s+(\d+)\s+RPM"
    fans = re.findall(fan_pattern, output, re.IGNORECASE)
    return [float(fan[1]) for fan in fans] if fans else []


def get_hdd_temps() -> Dict[str, float]:
    """获取硬盘温度"""
    hdd_temps = {}
    lsblk_path = "/usr/bin/lsblk"
    
    sata_disks_cmd = f"{lsblk_path} -d -o NAME | grep -E '^sd[a-z]$' || true"
    sata_disks = run_command(sata_disks_cmd).split()
    if sata_disks:
        for disk in sata_disks:
            disk = disk.strip()
            if not disk:
                continue
            output = run_command(f'sudo -n hddtemp /dev/{disk}')
            if output:
                match = re.search(r"(\d+)°C", output)
                if match:
                    hdd_temps[f"sata/{disk}"] = float(match.group(1))
    
    nvme_disks_cmd = f"{lsblk_path} -d -o NAME | grep -E '^nvme' || true"
    nvme_disks = run_command(nvme_disks_cmd).split()
    if nvme_disks:
        for disk in nvme_disks:
            disk = disk.strip()
            if not disk:
                continue
            output = run_command(f'sudo -n smartctl -A /dev/{disk}')
            temp_matches = re.findall(
                r"Temperature Sensor \d+:\s*(\d+)\s*Celsius", 
                output, 
                re.IGNORECASE
            )
            if temp_matches:
                hdd_temps[f"nvme/{disk}"] = float(temp_matches[0])
            else:
                temp_match = re.search(
                    r"Temperature:\s*(\d+)\s*Celsius", 
                    output, 
                    re.IGNORECASE
                )
                if temp_match:
                    hdd_temps[f"nvme/{disk}"] = float(temp_match.group(1))
    
    return hdd_temps


def get_cpu_usage() -> Optional[float]:
    """获取CPU使用率（最近1秒平均，依赖mpstat）"""
    try:
        output = run_command("mpstat 1 1 -u | awk 'END {print $NF}'")
        if not output:
            return None
        idle_percent = float(output.strip())
        return round(100.0 - idle_percent, 1)
    except Exception as e:
        print(f"获取CPU使用率失败: {e}", file=sys.stderr)
        return None


def get_memory_usage() -> Optional[float]:
    """获取内存使用率（百分比，兼容中英文free输出）"""
    try:
        output = run_command("free -t | awk '/内存：|Mem:/ {printf \"%.1f\", ($3/$2)*100}'")
        return round(float(output), 1) if output else None
    except Exception as e:
        print(f"获取内存使用率失败: {e}", file=sys.stderr)
        return None


def get_disk_usage() -> Optional[float]:
    """获取根目录(/)磁盘使用率（百分比，基于df命令）"""
    try:
        output = run_command("df --output=pcent / | tail -1 | tr -d '%'")
        return round(float(output), 1) if output else None
    except Exception as e:
        print(f"获取根目录磁盘使用率失败: {e}", file=sys.stderr)
        return None


def get_home_disk_usage() -> Optional[float]:
    """获取Home目录(/home)磁盘使用率（百分比，基于df命令）"""
    try:
        output = run_command("df --output=pcent /home | tail -1 | tr -d '%'")
        return round(float(output), 1) if output else None
    except Exception as e:
        print(f"获取Home磁盘使用率失败: {e}", file=sys.stderr)
        return None


class HardwareMonitorApp:
    """桌面窗口监控应用"""
    def __init__(self, root):
        self.root = root
        self.root.title("硬件状态监控")
        self.root.geometry("380x350")  # 扩大窗口容纳更多内容
        
        # 按新顺序初始化标签（时间→CPU使用率→内存→CPU温度→风扇→根目录→Home→硬盘→电池）
        self.timestamp_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.cpu_usage_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.memory_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.cpu_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.fan_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.disk_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.home_disk_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.hdd_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        self.battery_label = tk.Label(root, font=("微软雅黑", 12), justify="left")
        
        # 按新顺序布局标签
        self.timestamp_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.cpu_usage_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.memory_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.cpu_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.fan_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.disk_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.home_disk_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.hdd_label.pack(anchor='w', padx=(8, 0), pady=5)
        self.battery_label.pack(anchor='w', padx=(8, 0), pady=5)
        
        # 初始更新数据
        self.update_data()
    
    def update_data(self):
        """按新顺序更新窗口数据"""
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        
        # 按新顺序获取指标
        cpu_usage = get_cpu_usage() or "N/A"
        memory_usage = get_memory_usage() or "N/A"
        cpu_temp = get_cpu_temp().get("cpu", "N/A")
        fans = get_fan_speeds()
        fan_str = ",".join(map(str, fans)) if fans else "N/A"
        root_disk = get_disk_usage() or "N/A"
        home_disk = get_home_disk_usage() or "N/A"
        hdds = get_hdd_temps()
        hdd_str = ",".join([f"{k}:{v}" for k, v in hdds.items()]) or "N/A"
        battery = get_battery_percentage() or "N/A"
        
        # 按新顺序更新标签文本
        self.timestamp_label.config(text=f"时间戳：{timestamp}")
        self.cpu_usage_label.config(text=f"CPU使用率(%)：{cpu_usage}")
        self.memory_label.config(text=f"内存使用率(%)：{memory_usage}")
        self.cpu_label.config(text=f"CPU温度(°C)：{cpu_temp}")
        self.fan_label.config(text=f"风扇转速(RPM)：{fan_str}")
        self.disk_label.config(text=f"根目录(/)使用率(%)：{root_disk}")
        self.home_disk_label.config(text=f"Home(/home)使用率(%)：{home_disk}")
        self.hdd_label.config(text=f"硬盘温度(°C)：{hdd_str}")
        self.battery_label.config(text=f"电池电量(%)：{battery}")
        
        # 5秒后再次更新
        self.root.after(5000, self.update_data)


def main():
    """主函数"""
    root = tk.Tk()
    app = HardwareMonitorApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()