import Foundation

/// 端侧推理引擎选择。
/// - mnn:Qwen + MNN + SME2(CPU),挑战赛考核路径,真机默认。
/// - mlx:Qwen + MLX(Metal GPU),兜底 / 对照。模拟器只有它可用。
nonisolated enum InferenceEngine: String, CaseIterable, Sendable {
    case mnn
    case mlx

    var displayName: String {
        switch self {
        case .mnn: return "MNN · CPU/SME2"
        case .mlx: return "MLX · GPU"
        }
    }

    /// 本构建/设备是否可用。MNN 仅 device 切片有真实内核,模拟器回退 MLX。
    var isAvailable: Bool {
        switch self {
        case .mlx: return true
        case .mnn: return MNNLLMBridge.isAvailable()
        }
    }

    // MARK: - 持久化(UserDefaults,跨 actor 安全)

    private static let key = "kk.inferenceEngine"

    /// 由偏好(可能是 .auto)解析出的、本次调用实际使用的具体引擎。
    /// AIRuntime / MeView 等消费方只看这个,永远拿到 .mnn 或 .mlx。
    /// 解析后仍做一次可用性兜底,保证总有可用引擎。
    static var current: InferenceEngine {
        let resolved = preference.resolved
        return resolved.isAvailable ? resolved : .mlx
    }

    /// 运行时探测:CPU 是否支持 SME2(A19/iPhone17+)。用于 UI 展示加速状态。
    /// CPU 能力进程内不变,缓存一次,避免每次 UI 刷新都做 sysctl 系统调用。
    static let cpuSupportsSME2: Bool = MNNLLMBridge.cpuSupportsSME2()

    // MARK: - 用户偏好(auto / mnn / mlx)

    /// 用户在设置页的选择。默认 .auto:按本机配置自动择优。
    /// 与具体引擎共用同一 UserDefaults key——历史写入的 "mnn"/"mlx" 仍兼容。
    static var preference: EnginePreference {
        get {
            let raw = UserDefaults.standard.string(forKey: key)
            return raw.flatMap(EnginePreference.init(rawValue:)) ?? .auto
        }
        set { UserDefaults.standard.set(newValue.rawValue, forKey: key) }
    }
}

/// 推理引擎的「用户偏好」,比具体引擎多一个 .auto。
/// - auto:按本机配置自动选——真机优先 MNN(考核路径,含 SME2/NEON),
///         MNN 不可用(模拟器)时回退 MLX。
nonisolated enum EnginePreference: String, CaseIterable, Sendable {
    case auto
    case mnn
    case mlx

    var displayName: String {
        switch self {
        case .auto: return "自动"
        case .mnn:  return InferenceEngine.mnn.displayName
        case .mlx:  return InferenceEngine.mlx.displayName
        }
    }

    /// 把偏好解析成具体引擎(不做可用性兜底,那一步留给 `InferenceEngine.current`)。
    var resolved: InferenceEngine {
        switch self {
        case .mnn:  return .mnn
        case .mlx:  return .mlx
        case .auto: return InferenceEngine.mnn.isAvailable ? .mnn : .mlx
        }
    }
}
