DFWatchThemeStore.swift 4.32 KB
//
//  DFWatchThemeStore.swift
//  hippo-watch-extension
//

import Foundation
import SwiftUI

enum DFWatchThemeStore {
    static func currentTheme() async -> WatchLocalTheme? {
        if let localTheme = storedTheme() {
            if localTheme.hasAllImages {
                return localTheme
            }
            guard let remoteTheme = await remoteTheme() else {
                return localTheme
            }
            let refreshedTheme = await WatchThemeLocalBuilder.makeLocalTheme(from: remoteTheme)
            save(refreshedTheme)
            return refreshedTheme
        }
        if let remoteTheme = await remoteTheme() {
            let localTheme = await WatchThemeLocalBuilder.makeLocalTheme(from: remoteTheme)
            save(localTheme)
            return localTheme
        }
        guard let legacyTheme = legacyRemoteTheme() else {
            return nil
        }
        let localTheme = await WatchThemeLocalBuilder.makeLocalTheme(from: legacyTheme)
        save(localTheme)
        return localTheme
    }

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

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

    private static func save(_ theme: WatchLocalTheme) {
        guard let data = try? JSONEncoder().encode(theme) else { return }
        AppGroupConstants.defaults?.set(data, forKey: AppGroupConstants.Key.myWatchTheme)
    }

    private static func remoteTheme() async -> WatchThemeModel? {
        guard let login = loginInfo(),
              let token = login.token,
              !token.isEmpty else {
            return nil
        }

        let baseUrl = login.baseUrl ?? "https://api.doublefeel.cn"
        guard let url = URL(
            string: baseUrl + "/client/doublefeel/theme/v2/watch_theme/active/"
        ) else {
            return nil
        }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue(token, forHTTPHeaderField: "access_token")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        do {
            let (data, response) = try await URLSession.shared.data(for: request)
            guard let httpResponse = response as? HTTPURLResponse,
                  (200..<300).contains(httpResponse.statusCode) else {
                return nil
            }
            return try JSONDecoder().decode(WatchThemeModel.self, from: data)
        } catch {
            return nil
        }
    }

    private static func loginInfo() -> UserInfoModel? {
        guard let data = AppGroupConstants.defaults?.data(
            forKey: AppGroupConstants.Key.myUserInfo
        ) else {
            return nil
        }
        return try? JSONDecoder().decode(UserInfoModel.self, from: data)
    }

}

extension WatchLocalTheme {
    func faceContent(for status: DFStressStatus) -> (title: String, image: Image?) {
        let title: String
        let data: Data?
        switch status {
        case .excellent:
            title = energeticDescription
            data = energeticImage
        case .normal:
            title = normalDescription
            data = normalImage
        case .little:
            title = slightStressfulDescription
            data = slightStressfulImage
        case .overloaded:
            title = stressfulDescription
            data = stressfulImage
        }

        guard let data, let uiImage = UIImage(data: data) else {
            return (title, nil)
        }
        return (title, Image(uiImage: uiImage))
    }
}