import Foundation
import IOKit

// MARK: - SMC 温度传感器（通过 AppleSMC + IOKit 读取 CPU / GPU 温度）

// ── SMC 结构体定义（必须精确匹配 AppleSMC kext 的 SMCParamStruct）──

private struct SMCVersion {
    var major: UInt8 = 0
    var minor: UInt8 = 0
    var build: UInt8 = 0
    var reserved: UInt8 = 0
    var release: UInt16 = 0
}

private struct SMCPLimitData {
    var version: UInt16 = 0
    var length: UInt16 = 0
    var cpuPLimit: UInt32 = 0
    var gpuPLimit: UInt32 = 0
    var memPLimit: UInt32 = 0
}

private struct SMCKeyInfoData {
    var dataSize: UInt32 = 0
    var dataType: UInt32 = 0
    var dataAttributes: UInt8 = 0
}

private typealias SMCBytes = (
    UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
    UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
    UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
    UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8
)

private struct SMCParamStruct {
    var key: UInt32 = 0
    var vers = SMCVersion()
    var pLimitData = SMCPLimitData()
    var keyInfo = SMCKeyInfoData()
    var padding: UInt16 = 0       // 关键！对齐填充
    var result: UInt8 = 0
    var status: UInt8 = 0
    var data8: UInt8 = 0          // 子 selector（5=读key, 9=查询key信息, 8=按索引取key）
    var data32: UInt32 = 0
    var bytes: SMCBytes = (
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
    )
}

// SMC 常量
private let kSMCHandleYPCEvent: UInt32 = 2
private let kSMCReadKey: UInt8 = 5
private let kSMCGetKeyInfo: UInt8 = 9
private let kSMCGetKeyFromIndex: UInt8 = 8
private let kSMCSuccess: UInt8 = 0

/// 非 actor 工具类：封装 SMC 通信细节
final class SMCSensor {
    static let shared = SMCSensor()

    private var conn: io_connect_t = 0
    private var cpuKeys: [String] = []
    private var gpuKeys: [String] = []
    private var scanned = false

    private init() {}

    deinit {
        if conn != 0 { IOServiceClose(conn) }
    }

    // MARK: - 公开接口

    func readCPUTemperature() -> Double? {
        return readMax(keys: cpuKeys)
    }

    func readGPUTemperature() -> Double? {
        return readMax(keys: gpuKeys)
    }

    // MARK: - 内部

    private func readMax(keys: [String]) -> Double? {
        guard openSMC() else { return nil }
        if !scanned { scanKeys() }

        var maxTemp: Double? = nil
        for key in keys {
            if let temp = readKey(key) {
                maxTemp = max(maxTemp ?? temp, temp)
            }
        }
        return maxTemp
    }

    private func openSMC() -> Bool {
        if conn != 0 { return true }

        let service = IOServiceGetMatchingService(
            kIOMainPortDefault,
            IOServiceMatching("AppleSMC")
        )
        guard service != 0 else { return false }

        let status = IOServiceOpen(service, mach_task_self_, 0, &conn)
        IOObjectRelease(service)

        return status == KERN_SUCCESS && conn != 0
    }

    // MARK: - 扫描可用温度 key

    private func scanKeys() {
        scanned = true
        guard conn != 0 else { return }

        // 先获取 key 总数
        let totalKeys = readKeyCount()
        guard totalKeys > 0 else { return }

        var cpu: [String] = []
        var gpu: [String] = []

        for i in 0..<totalKeys {
            guard let keyName = keyAtIndex(i),
                  let (dataType, _) = keyInfo(keyName) else { continue }

            // 只关心浮点或 sp78 类型的温度 key
            let isTempType = (dataType == "flt " || dataType == "sp78")
            guard isTempType else { continue }

            if keyName.hasPrefix("Tp") {
                cpu.append(keyName)
            } else if keyName.hasPrefix("Tg") {
                gpu.append(keyName)
            }
        }

        if !cpu.isEmpty { cpuKeys = cpu }
        if !gpu.isEmpty { gpuKeys = gpu }
    }

    // MARK: - SMC 通信原语

    private func readKeyCount() -> Int {
        var input = SMCParamStruct()
        input.key = fourCharCode("#KEY")
        input.keyInfo.dataSize = 4
        input.keyInfo.dataType = fourCharCode("ui32")
        input.data8 = kSMCReadKey

        guard let output = callSMC(&input),
              output.result == kSMCSuccess else { return 0 }

        let raw = UInt32(output.bytes.0) << 24
                | UInt32(output.bytes.1) << 16
                | UInt32(output.bytes.2) << 8
                | UInt32(output.bytes.3)
        return Int(raw)
    }

    private func keyAtIndex(_ index: Int) -> String? {
        var input = SMCParamStruct()
        input.data8 = kSMCGetKeyFromIndex
        input.data32 = UInt32(index)

        guard let output = callSMC(&input),
              output.result == kSMCSuccess else { return nil }

        return charCodeToString(output.key)
    }

    /// 查询 key 的类型和大小，返回 (dataType, dataSize)
    private func keyInfo(_ keyName: String) -> (String, UInt32)? {
        var input = SMCParamStruct()
        input.key = fourCharCode(keyName)
        input.data8 = kSMCGetKeyInfo

        guard let output = callSMC(&input),
              output.result == kSMCSuccess else { return nil }

        return (charCodeToString(output.keyInfo.dataType),
                output.keyInfo.dataSize)
    }

    /// 读取 key 的值（自动适配 flt / sp78 类型）
    private func readKey(_ keyName: String) -> Double? {
        guard let (dataType, dataSize) = keyInfo(keyName) else { return nil }

        var input = SMCParamStruct()
        input.key = fourCharCode(keyName)
        input.keyInfo.dataSize = dataSize
        input.keyInfo.dataType = fourCharCode(dataType)
        input.data8 = kSMCReadKey

        guard let output = callSMC(&input),
              output.result == kSMCSuccess else { return nil }

        switch dataType {
        case "flt ":
            // 32-bit IEEE 754 float（小端序）
            let raw = UInt32(output.bytes.0)
                    | (UInt32(output.bytes.1) << 8)
                    | (UInt32(output.bytes.2) << 16)
                    | (UInt32(output.bytes.3) << 24)
            return Double(Float(bitPattern: raw))

        case "sp78":
            // 16-bit signed fixed-point
            let sign = (output.bytes.0 & 0x80) == 0 ? 1.0 : -1.0
            return sign * (Double(output.bytes.0 & 0x7F) + Double(output.bytes.1) / 256.0)

        default:
            return nil
        }
    }

    /// 发送 SMCParamStruct 到 AppleSMC
    private func callSMC(_ input: inout SMCParamStruct) -> SMCParamStruct? {
        var output = SMCParamStruct()
        let size = MemoryLayout<SMCParamStruct>.stride  // 80
        var outputSize = size

        let result = IOConnectCallStructMethod(
            conn,
            kSMCHandleYPCEvent,
            &input, size,
            &output, &outputSize
        )

        guard result == KERN_SUCCESS else { return nil }
        return output
    }

    // MARK: - 工具

    private func fourCharCode(_ string: String) -> UInt32 {
        var result: UInt32 = 0
        for char in string.utf8 {
            result = (result << 8) | UInt32(char)
        }
        return result
    }

    private func charCodeToString(_ code: UInt32) -> String {
        let c1 = UInt8((code >> 24) & 0xFF)
        let c2 = UInt8((code >> 16) & 0xFF)
        let c3 = UInt8((code >> 8) & 0xFF)
        let c4 = UInt8(code & 0xFF)
        return String(UnicodeScalar(c1))
             + String(UnicodeScalar(c2))
             + String(UnicodeScalar(c3))
             + String(UnicodeScalar(c4))
    }
}
