import Foundation
import IOKit
import Darwin

// MARK: - 系统信息数据结构

struct SystemInfo {
    var cpuUsage: Double          // 0-100
    var cpuCores: Int
    var cpuModel: String

    var gpuUsage: Double?         // 0-100 (部分 Mac 可能为 nil)
    var gpuModel: String

    var memoryTotal: UInt64       // bytes
    var memoryUsed: UInt64
    var memoryUsagePercent: Double

    var swapTotal: UInt64
    var swapUsed: UInt64
    var swapUsagePercent: Double
    var swapIns: UInt64
    var swapOuts: UInt64

    var topCPUProcesses: [ProcessInfo]
    var topMemoryProcesses: [ProcessInfo]

    struct ProcessInfo: Identifiable {
        let id = UUID()
        let pid: Int32
        let name: String
        let cpuPercent: Double
        let memoryBytes: UInt64
    }
}

// MARK: - 系统数据采集引擎

@MainActor
final class SystemMonitor: ObservableObject {
    @Published var info = SystemInfo(
        cpuUsage: 0, cpuCores: 0, cpuModel: "",
        gpuUsage: nil, gpuModel: "",
        memoryTotal: 0, memoryUsed: 0, memoryUsagePercent: 0,
        swapTotal: 0, swapUsed: 0, swapUsagePercent: 0,
        swapIns: 0, swapOuts: 0,
        topCPUProcesses: [], topMemoryProcesses: []
    )

    private var timer: Timer?
    private var previousCPU: host_cpu_load_info?

    // 采集频率
    var interval: TimeInterval = 1.5 {
        didSet { restartTimer() }
    }
    var topN: Int = 5

    nonisolated init() {}

    // MARK: - 启动 / 停止

    func start() {
        fetch() // 立即拉一次
        restartTimer()
    }

    func stop() {
        timer?.invalidate()
        timer = nil
    }

    private func restartTimer() {
        timer?.invalidate()
        timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in
            Task { @MainActor in
                self?.fetch()
            }
        }
        // 允许 timer 在 tracking 模式下也触发（防止拖拽窗口时卡顿不更新）
        if let t = timer {
            RunLoop.current.add(t, forMode: .common)
        }
    }

    // MARK: - 数据采集

    func fetch() {
        let cpu = fetchCPU()
        let gpu = fetchGPU()
        let mem = fetchMemory()
        let swap = fetchSwap()
        let procs = fetchTopProcesses(count: topN)

        info = SystemInfo(
            cpuUsage: cpu.usage,
            cpuCores: cpu.cores,
            cpuModel: cpu.model,
            gpuUsage: gpu.usage,
            gpuModel: gpu.model,
            memoryTotal: mem.total,
            memoryUsed: mem.used,
            memoryUsagePercent: mem.percent,
            swapTotal: swap.total,
            swapUsed: swap.used,
            swapUsagePercent: swap.percent,
            swapIns: swap.ins,
            swapOuts: swap.outs,
            topCPUProcesses: procs.cpu,
            topMemoryProcesses: procs.mem
        )
    }

    // MARK: - CPU

    private func fetchCPU() -> (usage: Double, cores: Int, model: String) {
        var size = mach_msg_type_number_t(MemoryLayout<host_cpu_load_info>.stride / MemoryLayout<integer_t>.stride)
        var info = host_cpu_load_info()
        let result = withUnsafeMutablePointer(to: &info) {
            $0.withMemoryRebound(to: integer_t.self, capacity: Int(size)) {
                host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, $0, &size)
            }
        }

        guard result == KERN_SUCCESS else {
            return (0, ProcessInfo.processInfo.activeProcessorCount, "Unknown")
        }

        let cores = ProcessInfo.processInfo.activeProcessorCount

        // 获取 CPU 型号
        var model = "Apple Silicon"
        var sysctlSize = 0
        sysctlbyname("machdep.cpu.brand_string", nil, &sysctlSize, nil, 0)
        if sysctlSize > 0 {
            var brand = [CChar](repeating: 0, count: sysctlSize)
            sysctlbyname("machdep.cpu.brand_string", &brand, &sysctlSize, nil, 0)
            model = String(cString: brand)
        }

        let user   = Double(info.cpu_ticks.0)
        let system = Double(info.cpu_ticks.1)
        let idle   = Double(info.cpu_ticks.2)
        let nice   = Double(info.cpu_ticks.3)

        var usage: Double = 0
        if let prev = previousCPU {
            let prevUser   = Double(prev.cpu_ticks.0)
            let prevSystem = Double(prev.cpu_ticks.1)
            let prevIdle   = Double(prev.cpu_ticks.2)
            let prevNice   = Double(prev.cpu_ticks.3)

            let totalTicks = (user - prevUser) + (system - prevSystem) + (idle - prevIdle) + (nice - prevNice)
            let usedTicks  = (user - prevUser) + (system - prevSystem) + (nice - prevNice)

            if totalTicks > 0 {
                usage = (usedTicks / totalTicks) * 100
            }
        }

        previousCPU = info
        return (min(usage, 100), cores, model)
    }

    // MARK: - GPU (通过 IOKit)

    private func fetchGPU() -> (usage: Double?, model: String) {
        var model = "Unknown GPU"
        var usage: Double? = nil

        // 查找 GPU 节点
        var iterator: io_iterator_t = 0
        let matchDict = IOServiceMatching("IOAccelerator")
        let result = IOServiceGetMatchingServices(kIOMainPortDefault, matchDict, &iterator)

        guard result == KERN_SUCCESS else { return (nil, "Unknown GPU") }

        var gpuEntry = IOIteratorNext(iterator)
        defer { IOObjectRelease(iterator) }

        while gpuEntry != 0 {
            // GPU 型号
            if let modelCF = IORegistryEntryCreateCFProperty(gpuEntry, "model" as CFString, kCFAllocatorDefault, 0) {
                if let modelStr = (modelCF.takeUnretainedValue() as? String) ?? (modelCF.takeUnretainedValue() as? Data).flatMap({ String(data: $0, encoding: .utf8) }) {
                    model = modelStr
                }
            }

            // 尝试读取性能统计
            if let perfCF = IORegistryEntryCreateCFProperty(gpuEntry, "PerformanceStatistics" as CFString, kCFAllocatorDefault, 0) {
                let perfDict = perfCF.takeUnretainedValue() as? [String: Any]
                // Apple Silicon 常见 key
                if let util = perfDict?["GPU Core Utilization"] as? Double {
                    usage = util * 100
                } else if let util = perfDict?["gpuCoreUtilization"] as? Double {
                    usage = util * 100
                } else if let util = perfDict?["Device Utilization %"] as? Int {
                    usage = Double(util)
                } else if let util = perfDict?["GPU Activity(%)"] as? Int {
                    usage = Double(util)
                }
            }

            IOObjectRelease(gpuEntry)
            gpuEntry = IOIteratorNext(iterator)
        }

        // 如果没有从 IOKit 拿到型号，用 sysctl
        if model == "Unknown GPU" {
            var size = 0
            sysctlbyname("hw.model", nil, &size, nil, 0)
            if size > 0 {
                var hwModel = [CChar](repeating: 0, count: size)
                sysctlbyname("hw.model", &hwModel, &size, nil, 0)
                let hw = String(cString: hwModel)
                if hw.hasPrefix("Mac") {
                    // Apple Silicon 通常显示为 "Apple M1/M2/M3..."
                    // 通过 IO 获取到的 model 更准确
                }
            }
        }

        return (usage, model)
    }

    // MARK: - 内存

    private func fetchMemory() -> (total: UInt64, used: UInt64, percent: Double) {
        let total = ProcessInfo.processInfo.physicalMemory

        var pageSize: vm_size_t = 0
        host_page_size(mach_host_self(), &pageSize)

        var vmStat = vm_statistics64()
        var count = mach_msg_type_number_t(MemoryLayout<vm_statistics64>.stride / MemoryLayout<integer_t>.stride)
        let result = withUnsafeMutablePointer(to: &vmStat) {
            $0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) {
                host_statistics64(mach_host_self(), HOST_VM_INFO64, $0, &count)
            }
        }

        guard result == KERN_SUCCESS else { return (total, 0, 0) }

        let activePages     = UInt64(vmStat.active_count)
        let wiredPages      = UInt64(vmStat.wire_count)
        let compressedPages = UInt64(vmStat.compressor_page_count)

        let pageSizeU64 = UInt64(pageSize)
        let usedRaw = (activePages + wiredPages + compressedPages) * pageSizeU64
        let percent = Double(usedRaw) / Double(total) * 100

        return (total, usedRaw, min(percent, 100))
    }

    // MARK: - Swap

    private func fetchSwap() -> (total: UInt64, used: UInt64, percent: Double, ins: UInt64, outs: UInt64) {
        var xsw = xsw_usage()
        var size = MemoryLayout<xsw_usage>.size
        let result = sysctlbyname("vm.swapusage", &xsw, &size, nil, 0)

        if result == 0 {
            return (xsw.xsu_total, xsw.xsu_used, xsw.xsu_total > 0 ? Double(xsw.xsu_used) / Double(xsw.xsu_total) * 100 : 0, 0, 0)
        }

        return (0, 0, 0, 0, 0)
    }

    // MARK: - 进程列表

    private func fetchTopProcesses(count: Int) -> (cpu: [SystemInfo.ProcessInfo], mem: [SystemInfo.ProcessInfo]) {
        // 使用 ps 命令获取进程 CPU / 内存排行
        // ps -eo pid,%cpu,rss,comm -r | head -n 30
        let cpuProcs = executePS(sortByCPU: true, count: count)
        let memProcs = executePS(sortByCPU: false, count: count)
        return (cpuProcs, memProcs)
    }

    private func executePS(sortByCPU: Bool, count: Int) -> [SystemInfo.ProcessInfo] {
        let sortFlag = sortByCPU ? "-r" : "-m"
        let command = "/bin/ps -eo pid,%cpu,rss,comm \(sortFlag) 2>/dev/null | head -n \(count + 1)"

        let process = Process()
        process.launchPath = "/bin/bash"
        process.arguments = ["-c", command]

        let pipe = Pipe()
        process.standardOutput = pipe
        process.standardError = Pipe()

        do {
            try process.run()
            process.waitUntilExit()
        } catch {
            return []
        }

        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        guard let output = String(data: data, encoding: .utf8) else { return [] }

        var results: [SystemInfo.ProcessInfo] = []
        let lines = output.components(separatedBy: "\n").dropFirst() // 去掉表头

        for line in lines {
            let trimmed = line.trimmingCharacters(in: .whitespaces)
            guard !trimmed.isEmpty else { continue }

            // 格式: PID %CPU RSS COMMAND
            var components = trimmed.components(separatedBy: .whitespaces)
            // 过滤空字符串
            components = components.filter { !$0.isEmpty }
            guard components.count >= 3 else { continue }

            let pidStr  = components[0]
            let cpuStr  = components[1]
            let rssStr  = components[2]
            let comm    = components.dropFirst(3).joined(separator: " ")

            guard let pid  = Int32(pidStr),
                  let cpu  = Double(cpuStr),
                  let rss  = UInt64(rssStr) else { continue }

            let rssBytes = rss * 1024 // RSS 单位是 KB

            results.append(SystemInfo.ProcessInfo(
                pid: pid,
                name: comm,
                cpuPercent: cpu,
                memoryBytes: rssBytes
            ))
        }

        return results
    }
}
