//
//  DataStore.swift
//  USB Meter
//
//  Created by Bogdan Timofte on 03/03/2020.
//  Copyright © 2020 Bogdan Timofte. All rights reserved.
//

import SwiftUI
import Combine
import CoreBluetooth

final class AppData : ObservableObject {
    private var bluetoothManagerNotification: AnyCancellable?
    private var meterStoreObserver: AnyCancellable?
    private var meterStoreCloudObserver: AnyCancellable?
    private let meterStore = MeterNameStore.shared

    init() {
        bluetoothManagerNotification = bluetoothManager.objectWillChange.sink { [weak self] _ in
            self?.scheduleObjectWillChange()
        }
        meterStoreObserver = NotificationCenter.default.publisher(for: .meterNameStoreDidChange)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.refreshMeterMetadata()
            }
        meterStoreCloudObserver = NotificationCenter.default.publisher(for: .meterNameStoreCloudStatusDidChange)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.scheduleObjectWillChange()
            }
    }

    let bluetoothManager = BluetoothManager()

    @Published var enableRecordFeature: Bool = true

    @Published var meters: [UUID:Meter] = [UUID:Meter]()

    var cloudAvailability: MeterNameStore.CloudAvailability {
        meterStore.currentCloudAvailability
    }

    func meterName(for macAddress: String) -> String? {
        meterStore.name(for: macAddress)
    }

    func setMeterName(_ name: String, for macAddress: String) {
        meterStore.upsert(macAddress: macAddress, name: name, temperatureUnitRawValue: nil)
    }

    func temperatureUnitPreference(for macAddress: String) -> TemperatureUnitPreference {
        let rawValue = meterStore.temperatureUnitRawValue(for: macAddress) ?? TemperatureUnitPreference.celsius.rawValue
        return TemperatureUnitPreference(rawValue: rawValue) ?? .celsius
    }

    func setTemperatureUnitPreference(_ preference: TemperatureUnitPreference, for macAddress: String) {
        meterStore.upsert(macAddress: macAddress, name: nil, temperatureUnitRawValue: preference.rawValue)
    }

    private func scheduleObjectWillChange() {
        DispatchQueue.main.async { [weak self] in
            self?.objectWillChange.send()
        }
    }

    private func refreshMeterMetadata() {
        DispatchQueue.main.async { [weak self] in
            guard let self else { return }
            var didUpdateAnyMeter = false
            for meter in self.meters.values {
                let mac = meter.btSerial.macAddress.description
                let displayName = self.meterName(for: mac) ?? mac
                if meter.name != displayName {
                    meter.updateNameFromStore(displayName)
                    didUpdateAnyMeter = true
                }

                let previousTemperaturePreference = meter.tc66TemperatureUnitPreference
                meter.reloadTemperatureUnitPreference()
                if meter.tc66TemperatureUnitPreference != previousTemperaturePreference {
                    didUpdateAnyMeter = true
                }
            }

            if didUpdateAnyMeter {
                self.scheduleObjectWillChange()
            }
        }
    }
}
