import Foundation

@Observable
final class AppSettings {
    private static let selectedTypeIDsKey   = "hp_selectedTypeIDs"
    private static let captureProfileKey = "hp_captureProfile"
    static let adaptiveTimeoutsEnabledKey = "hp_adaptiveTimeoutsEnabled"
    static let simplifiedUIModeEnabledKey = "hp_simplifiedUIModeEnabled"
    static let typeDetailCacheBackfillVersionKey = "hp_typeDetailCacheBackfillVersion"
    static let currentTypeDetailCacheBackfillVersion = 2

    private enum CaptureProfile: String {
        case allAvailable
        case core
        case custom
    }

    var selectedTypeIDs: Set<String> {
        didSet { persistTypes() }
    }

    var adaptiveTimeoutsEnabled: Bool {
        didSet {
            UserDefaults.standard.set(adaptiveTimeoutsEnabled, forKey: Self.adaptiveTimeoutsEnabledKey)
        }
    }

    var simplifiedUIModeEnabled: Bool {
        didSet {
            UserDefaults.standard.set(simplifiedUIModeEnabled, forKey: Self.simplifiedUIModeEnabledKey)
        }
    }

    var selectedTypeCount: Int {
        selectedTypeIDs.count
    }

    var defaultTypeCount: Int {
        Self.defaultTypeIDs.count
    }

    var availableTypeCount: Int {
        Self.availableTypeIDs.count
    }

    var usesDefaultTypeProfile: Bool {
        selectedTypeIDs == Self.defaultTypeIDs
    }

    var usesAllAvailableTypeProfile: Bool {
        selectedTypeIDs == Self.availableTypeIDs
    }

    var captureProfileDescription: String {
        if usesAllAvailableTypeProfile { return "All available" }
        if usesDefaultTypeProfile { return "Core" }
        return "Custom"
    }

    static var currentDeviceID: String {
        KeychainService.resolveDeviceID(swiftDataStoreIsEmpty: false).id
    }

    private static var defaultTypeIDs: Set<String> {
        Set(HealthKitService.allTypes.filter { $0.isEnabledByDefault }.map(\.id))
    }

    private static var availableTypeIDs: Set<String> {
        Set(HealthKitService.allTypes.compactMap { type in
            type.objectType == nil ? nil : type.id
        })
    }

    init() {
        let persistedTypeIDs: Set<String>?
        if let data = UserDefaults.standard.data(forKey: Self.selectedTypeIDsKey),
           let ids  = try? JSONDecoder().decode([String].self, from: data) {
            persistedTypeIDs = Set(ids)
        } else {
            persistedTypeIDs = nil
        }

        let persistedProfile = UserDefaults.standard.string(forKey: Self.captureProfileKey)
            .flatMap(CaptureProfile.init(rawValue:))

        switch persistedProfile {
        case .allAvailable:
            selectedTypeIDs = Self.availableTypeIDs
        case .core:
            selectedTypeIDs = Self.defaultTypeIDs
        case .custom:
            selectedTypeIDs = persistedTypeIDs ?? Self.availableTypeIDs
        case nil:
            if let persistedTypeIDs, persistedTypeIDs != Self.defaultTypeIDs {
                selectedTypeIDs = persistedTypeIDs
                Self.persistCaptureProfile(.custom)
            } else {
                selectedTypeIDs = Self.availableTypeIDs
                Self.persistCaptureProfile(.allAvailable)
            }
        }

        if UserDefaults.standard.object(forKey: Self.adaptiveTimeoutsEnabledKey) == nil {
            adaptiveTimeoutsEnabled = true
        } else {
            adaptiveTimeoutsEnabled = UserDefaults.standard.bool(forKey: Self.adaptiveTimeoutsEnabledKey)
        }

        simplifiedUIModeEnabled = UserDefaults.standard.bool(forKey: Self.simplifiedUIModeEnabledKey)
    }

    func isEnabled(_ type: MonitoredType) -> Bool { selectedTypeIDs.contains(type.id) }

    func toggle(_ type: MonitoredType) {
        Self.persistCaptureProfile(.custom)
        if selectedTypeIDs.contains(type.id) { selectedTypeIDs.remove(type.id) }
        else { selectedTypeIDs.insert(type.id) }
    }

    func selectDefaultTypes() {
        Self.persistCaptureProfile(.core)
        selectedTypeIDs = Self.defaultTypeIDs
    }

    func selectAllAvailableTypes() {
        Self.persistCaptureProfile(.allAvailable)
        selectedTypeIDs = Self.availableTypeIDs
    }

    func clearSelectedTypes() {
        Self.persistCaptureProfile(.custom)
        selectedTypeIDs = []
    }

    private func persistTypes() {
        UserDefaults.standard.set(try? JSONEncoder().encode(Array(selectedTypeIDs)),
                                  forKey: Self.selectedTypeIDsKey)
    }

    private static func persistCaptureProfile(_ profile: CaptureProfile) {
        UserDefaults.standard.set(profile.rawValue, forKey: Self.captureProfileKey)
    }
}
