import Foundation
import UIKit

@Observable
final class AppSettings {
    private static let selectedTypeIDsKey   = "hp_selectedTypeIDs"
    private static let selectedDeviceIDsKey = "hp_selectedDeviceIDs"
    static let adaptiveTimeoutsEnabledKey = "hp_adaptiveTimeoutsEnabled"

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

    var selectedDeviceIDs: Set<String> {
        didSet { persistDevices() }
    }

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

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

    init() {
        if let data = UserDefaults.standard.data(forKey: Self.selectedTypeIDsKey),
           let ids  = try? JSONDecoder().decode([String].self, from: data) {
            selectedTypeIDs = Set(ids)
        } else {
            selectedTypeIDs = Set(HealthKitService.allTypes.filter { $0.isEnabledByDefault }.map { $0.id })
        }

        if let data = UserDefaults.standard.data(forKey: Self.selectedDeviceIDsKey),
           let ids  = try? JSONDecoder().decode([String].self, from: data) {
            let storedIDs = Set(ids)
            let oldCurrentID = UIDevice.current.identifierForVendor?.uuidString
            if let oldCurrentID, storedIDs == [oldCurrentID] {
                selectedDeviceIDs = [Self.currentDeviceID]
            } else {
                selectedDeviceIDs = storedIDs
            }
        } else {
            let currentID = Self.currentDeviceID
            selectedDeviceIDs = currentID.isEmpty ? [] : [currentID]
        }

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

        persistDevices()
    }

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

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

    func toggleDevice(_ id: String) {
        if selectedDeviceIDs.contains(id) { selectedDeviceIDs.remove(id) }
        else { selectedDeviceIDs.insert(id) }
    }

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

    private func persistDevices() {
        UserDefaults.standard.set(try? JSONEncoder().encode(Array(selectedDeviceIDs)),
                                  forKey: Self.selectedDeviceIDsKey)
    }
}
