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

import Foundation
import WidgetKit
import Combine

@MainActor
final class WatchUserinfoManager: ObservableObject {
    static let share = WatchUserinfoManager()

    private let service = UserInfoService()
    private let themeService = ThemeService()

    var isLogin: Bool{
        return token?.count ?? 0 > 0
    }

    var isMeVip: Bool? {
        vipInfo?.isAvaiableVip
    }
    var baseUrl: String{
        myUserInfo?.baseUrl ?? "https://api.doublefeel.cn"
    }

    var token: String?{
        myUserInfo?.token
    }

    private(set) var myUserInfo: UserInfoModel?

    @Published var displayFriend: HAppFriend?
    @Published var vipInfo: HVipInfo?
    @Published var showRealtimeHRV: Bool
    @Published var interactionActionType: InteractionActionType = .stick

    var myUserinfo: UserInfo? {
        nil
    }

    // 表盘主题
    @Published var myTheme: WatchLocalTheme?
    @Published var otherTheme: WatchLocalTheme?

    private var didSetup = false

    var displayType: HealthType{
        showRealtimeHRV ? .realtime_stress : .hrv
    }

    private init(){
        showRealtimeHRV = AppGroupConstants.defaults?.bool(
            forKey: AppGroupConstants.Key.showRealtimeHRV
        ) ?? false

        guard let data = AppGroupConstants.defaults?.data(forKey: AppGroupConstants.Key.myUserInfo) else{
            return
        }
        do{
            self.myUserInfo = try JSONDecoder().decode(UserInfoModel.self, from: data)
        }catch{}
    }

    func setup() {
        guard !didSetup else { return }
        didSetup = true

        Task { @MainActor [weak self] in
            await Task.yield()
            self?.reloadLocalFriend()
            self?.loadLocalWatchTheme()

            self?.reloadLogin()
        }
    }

    func login(userInfo: UserInfoModel){
        guard let _ = userInfo.token, let _ = userInfo.userId, let _ = userInfo.baseUrl else{
            return
        }
        self.myUserInfo = userInfo

        reloadLogin()
    }

    @MainActor
    func reloadLogin(){
        // 刷新所有数据
        if isLogin{
            reloadVip()
            reloadFriend()
        }
        if let id = self.myUserInfo?.userId {
            ThinkSDKUtil.login(id)
        }
    }
    @MainActor
    func logout(){
        myUserInfo = nil
        displayFriend = nil
        vipInfo = nil
        showRealtimeHRV = false
        myTheme = nil
        otherTheme = nil
        AppGroupConstants.clearUserSessionData()

        ThinkSDKUtil.logout()
    }

    @MainActor
    func switchHRVType(to realtime: Bool){
        showRealtimeHRV = realtime
        AppGroupConstants.defaults?.set(showRealtimeHRV, forKey: AppGroupConstants.Key.showRealtimeHRV)
        // 刷新数据
        WatchDataManager.share.getLatestHealthData()
        WatchDataManager.share.getMyLatestData()
        if let _ = displayFriend?.friend_user_id{
            WatchDataManager.share.getFriendLatestData()
        }
    }

    func reloadTheme(){
        loadLocalWatchTheme()
        Task {
            await loadRemoteWatchTheme()
        }
        WidgetCenter.shared.reloadTimelines(ofKind: HippoWidgetKind.hrv.rawValue)
    }

    func applySyncedTheme(_ theme: WatchLocalTheme) {
        saveTheme(theme, key: AppGroupConstants.Key.myWatchTheme)
        myTheme = theme
        objectWillChange.send()
        WidgetCenter.shared.reloadTimelines(ofKind: HippoWidgetKind.hrv.rawValue)
    }

    func deleteLocalTheme(){
        deleteTheme(key: AppGroupConstants.Key.myWatchTheme)
        myTheme = nil
        objectWillChange.send()
        WidgetCenter.shared.reloadTimelines(ofKind: HippoWidgetKind.hrv.rawValue)
    }
    //MARK: - 表盘主题
    @MainActor
    private func loadLocalWatchTheme(){
        myTheme = loadTheme(key: AppGroupConstants.Key.myWatchTheme)
        otherTheme = loadTheme(key: AppGroupConstants.Key.otherWatchTheme)
        WidgetCenter.shared.reloadTimelines(ofKind: HippoWidgetKind.hrv.rawValue)
    }

    private func loadRemoteWatchTheme() async{
        let myRemoteTheme = try? await themeService.getCurrentTheme(userId: nil)
        var otherRemoteTheme: WatchThemeModel?
        if let userId = displayFriend?.user_id{
            otherRemoteTheme = try? await themeService.getCurrentTheme(userId: userId)
        }

        if (myTheme?.hasAllImages != true), let myRemoteTheme {
            let theme = await WatchThemeLocalBuilder.makeLocalTheme(from: myRemoteTheme)
            myTheme = theme
            saveTheme(theme, key: AppGroupConstants.Key.myWatchTheme)
        } else if myTheme == nil,
                  let legacyTheme = legacyRemoteTheme(key: AppGroupConstants.Key.myWatchTheme) {
            let theme = await WatchThemeLocalBuilder.makeLocalTheme(from: legacyTheme)
            myTheme = theme
            saveTheme(theme, key: AppGroupConstants.Key.myWatchTheme)
        }

        if let otherRemoteTheme {
            let theme = await WatchThemeLocalBuilder.makeLocalTheme(from: otherRemoteTheme)
            if theme != otherTheme {
                otherTheme = theme
                saveTheme(theme, key: AppGroupConstants.Key.otherWatchTheme)
            }
        } else if displayFriend?.user_id != nil,
                  otherTheme == nil,
                  let legacyTheme = legacyRemoteTheme(key: AppGroupConstants.Key.otherWatchTheme) {
            let theme = await WatchThemeLocalBuilder.makeLocalTheme(from: legacyTheme)
            otherTheme = theme
            saveTheme(theme, key: AppGroupConstants.Key.otherWatchTheme)
        } else {
            otherTheme = nil
            AppGroupConstants.defaults?.removeObject(forKey: AppGroupConstants.Key.otherWatchTheme)
        }

        WidgetCenter.shared.reloadTimelines(ofKind: HippoWidgetKind.hrv.rawValue)
    }

    private func loadTheme(key: String) -> WatchLocalTheme? {
        guard let data = AppGroupConstants.defaults?.data(forKey: key) else { return nil }
        if let theme = try? JSONDecoder().decode(WatchLocalTheme.self, from: data) {
            return theme
        }
        return nil
    }

    private func legacyRemoteTheme(key: String) -> WatchThemeModel? {
        if let data = AppGroupConstants.defaults?.data(forKey: key),
           let theme = try? JSONDecoder().decode(WatchThemeModel.self, from: data) {
            return theme
        }
        if let jsonString = AppGroupConstants.defaults?.string(forKey: key),
           let data = jsonString.data(using: .utf8),
           let theme = try? JSONDecoder().decode(WatchThemeModel.self, from: data) {
            return theme
        }
        return nil
    }

    private func saveTheme(_ theme: WatchLocalTheme, key: String) {
        guard let data = try? JSONEncoder().encode(theme) else { return }
        AppGroupConstants.defaults?.set(data, forKey: key)
    }

    private func deleteTheme(key: String){
        AppGroupConstants.defaults?.removeObject(forKey: key)
        AppGroupConstants.defaults?.synchronize()
    }

    //MARK: - 我的信息
//    private func reloadUserInfo(){
//        guard isLogin else{
//            return
//        }
//        Task{
//            myUserInfo = await service.getMyUserInfo()
//            guard let myUserInfo else{
//                AppGroupConstants.defaults?.removeObject(forKey: AppGroupConstants.Key.myUserInfo)
//                return
//            }
//            do{
//                let data = try JSONEncoder().encode(myUserInfo)
//                AppGroupConstants.defaults?.set(data, forKey: AppGroupConstants.Key.myUserInfo)
//            }catch{}
//        }
//    }

    private func reloadFriend(){
        guard isLogin else{
            return
        }
        Task{
            let friendList = await service.getFriendInfo()
            let friend = friendList?.list?.first(where: { $0.show_in_dial == 1 })
            if let _ = friend?.friend_user_id{
                WatchDataManager.share.getFriendLatestData()
            }
            self.saveOtherData(displayFriend: friend)

           await loadRemoteWatchTheme()
        }
    }
    func reloadVip(){
        guard isLogin else{
            return
        }
        Task{
            let vipInfo = await service.getVipInfo()
            self.saveVip(info: vipInfo)
        }
    }


    //MARK: - OtherData
    private func reloadLocalFriend(){
        if let data = AppGroupConstants.defaults?.data(forKey: AppGroupConstants.Key.showOtherUserInfo),
           let model = try? JSONDecoder().decode(HAppFriend.self, from: data) {
            self.displayFriend = model
        }else{
            self.displayFriend = nil
        }
    }
    private func saveOtherData(displayFriend: HAppFriend?){
        self.displayFriend = displayFriend

        guard let displayFriend else{
            AppGroupConstants.defaults?.removeObject(forKey: AppGroupConstants.Key.showOtherUserInfo)
            return
        }
        do{
            let data = try JSONEncoder().encode(displayFriend)
            AppGroupConstants.defaults?.set(data, forKey: AppGroupConstants.Key.showOtherUserInfo)
        }catch{}
    }

    //MARK: - VIP
    private func reloadVipInfo(){
        if let data = AppGroupConstants.defaults?.data(forKey: AppGroupConstants.Key.vipInfo),
           let model = try? JSONDecoder().decode(HVipInfo.self, from: data) {
            self.vipInfo = model
        }else{
            self.vipInfo = nil
        }
        reloadVip()
    }
    private func saveVip(info: HVipInfo?){
        self.vipInfo = info
        if let info{
            do{
                let data = try JSONEncoder().encode(info)
                AppGroupConstants.defaults?.set(data, forKey: AppGroupConstants.Key.vipInfo)
            }catch{}
        }else{
            AppGroupConstants.defaults?.removeObject(forKey: AppGroupConstants.Key.vipInfo)
        }
    }

}