StatusComparisonViewModel.swift 7.17 KB
//
//  StatusComparisonViewModel.swift
//  hippo-watch Watch App
//
//  Created by shihao on 2025/7/8.
//

import Foundation
import SwiftUI
import ClockKit
import SDWebImage
import Combine

class StatusComparisonViewModel: ObservableObject {
    
    @Published var currentInteractionAction: InteractionActionType?
    @Published var myWatchTheme: WatchThemeModel?
    @Published var otherWatchTheme: WatchThemeModel?
    
    init() {
        refreshTheme()
    }
    
    func onInteractionClick() {
        Task {
            do {
                setupCurrerntInteractionAnimation()
                let _ = try await InteractionService().sendInteraction(actionType: WatchUserinfoManager.share.interactionActionType)
            } catch {
                print(error.getToastErrorDescription())
            }
        }
    }
    
    private func setupCurrerntInteractionAnimation() {
        currentInteractionAction = WatchUserinfoManager.share.interactionActionType
        Task {
            try? await Task.sleep(nanoseconds: 4 * 1_000_000_000)
            await MainActor.run {
                self.currentInteractionAction = nil
            }
        }
    }
    
    func refreshTheme() {
        getMyWatchTheme()
        getOtherWatchTheme()
    }
    
    private func getMyWatchTheme() {
        // 1️⃣ 先从 UserDefaults 读取缓存
        let defaults = AppGroupConstants.defaults
        if let jsonString = defaults?.string(forKey: AppGroupConstants.Key.myWatchTheme),
           let jsonData = jsonString.data(using: .utf8),
           let cachedModel = try? JSONDecoder().decode(WatchThemeModel.self, from: jsonData) {
            // 先展示缓存数据
            myWatchTheme = cachedModel
        }
        
        // 2️⃣ 再异步调用接口刷新
        Task {
            if let result = try? await ThemeService().getCurrentTheme(isOther: false) {
                await MainActor.run {
                    self.myWatchTheme = result
                }
                await saveImageToSharedContainer()
                await MainActor.run {
                    reloadAllComplications()
                }
                
                // 3️⃣ 将最新数据写入 UserDefaults
                do {
                    let data = try JSONEncoder().encode(result)
                    if let jsonString = String(data: data, encoding: .utf8) {
                        print(jsonString)
                        defaults?.set(jsonString, forKey: "myWatchTheme")
                    }
                }catch {
                    print(error.getToastErrorDescription())
                }
                
            }
        }
    }
    
    private func getOtherWatchTheme() {
        guard WatchUserinfoManager.share.myUserinfo?.isPaired == true else { return }
        
        // 1️⃣ 先从 UserDefaults 读取缓存
        let defaults = AppGroupConstants.defaults
        if let jsonString = defaults?.string(forKey: AppGroupConstants.Key.otherWatchTheme),
           let jsonData = jsonString.data(using: .utf8),
           let cachedModel = try? JSONDecoder().decode(WatchThemeModel.self, from: jsonData) {
            // 先展示缓存数据
            otherWatchTheme = cachedModel
        }
        
        // 2️⃣ 再异步调用接口刷新
        Task {
            if let result = try? await ThemeService().getCurrentTheme(isOther: true) {
                await MainActor.run {
                    self.otherWatchTheme = result
                }
                
                // 3️⃣ 将最新数据写入 UserDefaults
                do {
                    let data = try JSONEncoder().encode(result)
                    if let jsonString = String(data: data, encoding: .utf8) {
                        print(jsonString)
                        defaults?.set(jsonString, forKey: "otherWatchTheme")
                    }
                }catch {
                    print(error.getToastErrorDescription())
                }
            }
        }
    }
    
    private func reloadAllComplications() {
#if os(watchOS)
        let server = CLKComplicationServer.sharedInstance()
        if let complications = server.activeComplications {
            for complication in complications {
                server.reloadTimeline(for: complication)
            }
        }
#endif
    }
    
    private func saveImageToSharedContainer() async {
        guard let myWatchTheme, myWatchTheme.isDefaultTheme != true else { return }
        
        // 将下载逻辑封装成 async 方法
        func downloadAndSave(_ url: URL?) async {
            await withCheckedContinuation { continuation in
                SDWebImageDownloader.shared.downloadImage(with: url) { image, data, error, _ in
                    if let data {
                        self.saveImageToAppGroup(data, fileName: url?.lastPathComponent)
                    }
                    continuation.resume()
                }
            }
        }
        
        await withTaskGroup(of: Void.self) { group in
            if let positiveImage = myWatchTheme.hrvStatusThumnailImageURL(hrvStatus: .fullOfEnergy) {
                group.addTask {
                    await downloadAndSave(positiveImage.url)
                }
            }
            if let normalImage = myWatchTheme.hrvStatusThumnailImageURL(hrvStatus: .normal) {
                group.addTask {
                    await downloadAndSave(normalImage.url)
                }
            }
            if let negativeImage = myWatchTheme.hrvStatusThumnailImageURL(hrvStatus: .overpressure) {
                group.addTask {
                    await downloadAndSave(negativeImage.url)
                }
            }
        }
        
        print("✅ 所有图片已下载完成并写入共享容器")
    }
    
    private func saveImageToAppGroup(_ data: Data, fileName: String?) {
        guard let fileName else {return}
        
        guard let containerURL = FileManager.default.containerURL(
            forSecurityApplicationGroupIdentifier: AppGroupConstants.identifier
        ) else {
            print("❌ App Group 容器不存在,请检查配置")
            return
        }
        
        // 清理文件名,移除可能的路径分隔符
        let cleanFileName = fileName.replacingOccurrences(of: "/", with: "_")
        let fileURL = containerURL.appendingPathComponent(cleanFileName)
        
        do {
            // 1. 确保容器目录存在
            if !FileManager.default.fileExists(atPath: containerURL.path) {
                try FileManager.default.createDirectory(
                    at: containerURL,
                    withIntermediateDirectories: true,
                    attributes: nil
                )
            }
            
            // 2. 如果文件已存在,先删除
            if FileManager.default.fileExists(atPath: fileURL.path) {
//                try FileManager.default.removeItem(at: fileURL)
                return
            }
            
            // 3. 写入文件
            try data.write(to: fileURL, options: [.atomic])
            
            print("✅ 图片保存成功: \(fileURL.lastPathComponent)")
            
        } catch {
            print("❌ 写入报错: \(error.localizedDescription)")
        }
    }
}