// LoopFollow
// StorageCurrentGlucoseStateProvider.swift

import Foundation

/// Reads the latest glucose state from LoopFollow's Storage and Observable layers.
/// This is the only file in the pipeline that is allowed to touch Storage.shared
/// or Observable.shared — all other layers read exclusively from this provider.
struct StorageCurrentGlucoseStateProvider: CurrentGlucoseStateProviding {
    // MARK: - Core Glucose

    var glucoseMgdl: Double? {
        guard let bg = Observable.shared.bg.value, bg > 0 else { return nil }
        return Double(bg)
    }

    var deltaMgdl: Double? {
        Storage.shared.lastDeltaMgdl.value
    }

    var projectedMgdl: Double? {
        Storage.shared.projectedBgMgdl.value
    }

    var updatedAt: Date? {
        guard let t = Storage.shared.lastBgReadingTimeSeconds.value else { return nil }
        return Date(timeIntervalSince1970: t)
    }

    var trendCode: String? {
        Storage.shared.lastTrendCode.value
    }

    // MARK: - Secondary Metrics

    var iob: Double? {
        Storage.shared.lastIOB.value
    }

    var cob: Double? {
        Storage.shared.lastCOB.value
    }

    // MARK: - Extended Metrics

    var override: String? {
        Observable.shared.override.value
    }

    var recBolus: Double? {
        Observable.shared.deviceRecBolus.value
    }

    var battery: Double? {
        Observable.shared.deviceBatteryLevel.value
    }

    var pumpBattery: Double? {
        Observable.shared.pumpBatteryLevel.value
    }

    var basalRate: String {
        Storage.shared.lastBasal.value
    }

    var pumpReservoirU: Double? {
        Storage.shared.lastPumpReservoirU.value
    }

    var autosens: Double? {
        Storage.shared.lastAutosens.value
    }

    var tdd: Double? {
        Storage.shared.lastTdd.value
    }

    var targetLowMgdl: Double? {
        Storage.shared.lastTargetLowMgdl.value
    }

    var targetHighMgdl: Double? {
        Storage.shared.lastTargetHighMgdl.value
    }

    var isfMgdlPerU: Double? {
        Storage.shared.lastIsfMgdlPerU.value
    }

    var carbRatio: Double? {
        Storage.shared.lastCarbRatio.value
    }

    var carbsToday: Double? {
        Storage.shared.lastCarbsToday.value
    }

    var profileName: String? {
        let raw = Storage.shared.lastProfileName.value
        return raw.isEmpty ? nil : raw
    }

    var sageInsertTime: TimeInterval {
        Storage.shared.sageInsertTime.value
    }

    var cageInsertTime: TimeInterval {
        Storage.shared.cageInsertTime.value
    }

    var iageInsertTime: TimeInterval {
        Storage.shared.iageInsertTime.value
    }

    var minBgMgdl: Double? {
        Storage.shared.lastMinBgMgdl.value
    }

    var maxBgMgdl: Double? {
        Storage.shared.lastMaxBgMgdl.value
    }

    // MARK: - Loop Status

    var isNotLooping: Bool {
        let lastLoopTime = Storage.shared.lastLoopTime.value
        guard lastLoopTime > 0, !Storage.shared.url.value.isEmpty else { return false }
        return Date().timeIntervalSince1970 - lastLoopTime >= 15 * 60
    }

    // MARK: - Renewal

    var showRenewalOverlay: Bool {
        #if targetEnvironment(macCatalyst)
            return false
        #else
            // iOS 17.2+ renews silently via push-to-start at the deadline, so the
            // pre-emptive 30-minute "tap to update" overlay would be misleading
            // during normal operation. Only show it once renewal has actually
            // failed (no token, bad creds, rate-limited) — that is genuinely
            // user-actionable. iOS 16.x keeps the time-based warning because
            // renewal there requires the user to foreground the app.
            if #available(iOS 17.2, *) {
                return Storage.shared.laRenewalFailed.value
            } else {
                let renewBy = Storage.shared.laRenewBy.value
                let now = Date().timeIntervalSince1970
                return renewBy > 0 && now >= renewBy - LiveActivityManager.renewalWarning
            }
        #endif
    }
}
