import Foundation
import HealthKit
import SwiftData
import UIKit
import os.log

private let logger = Logger(subsystem: "ro.xdev.healthprobe", category: "HealthKitService")

enum TypeCategory: String, CaseIterable {
    case activity    = "Activity"
    case heart       = "Heart"
    case respiratory = "Respiratory"
    case sleep       = "Sleep"
    case hearing     = "Hearing"
    case body        = "Body"
}

struct MonitoredType: Identifiable, @unchecked Sendable {
    let id: String
    let displayName: String
    let category: TypeCategory
    let isEnabledByDefault: Bool
    let objectType: HKObjectType?   // nil = unsupported on this OS/device
}

extension Array where Element == MonitoredType {
    func sortedByFetchDisplayNameDescending() -> [MonitoredType] {
        sorted {
            let displayNameOrder = $0.displayName.localizedCaseInsensitiveCompare($1.displayName)
            if displayNameOrder == .orderedSame {
                return $0.id > $1.id
            }
            return displayNameOrder == .orderedDescending
        }
    }
}

final class HealthKitService {
    static let shared = HealthKitService()
    let store = HKHealthStore()
    private let archiveStore: HealthArchiveStore

    private init(archiveStore: HealthArchiveStore = SQLiteHealthArchiveStore.shared) {
        self.archiveStore = archiveStore
    }

    static let allTypes: [MonitoredType] = buildAllTypes()

    static let defaultInitialTimeoutSeconds: TimeInterval = LocalMetricTimeoutProfile.defaultInitialTimeout
    static let maximumTimeoutSeconds: TimeInterval = LocalMetricTimeoutProfile.maximumTimeout
    static let fullHistoryImportTimeoutSeconds: TimeInterval = 30 * 60
    // Prevents 3N simultaneous HK queries from exhausting resources at N=20 types.
    static let maxConcurrentTypeFetches = 6

    var isAvailable: Bool { HKHealthStore.isHealthDataAvailable() }

    var hasRequestedPermissionsBefore: Bool {
        get {
            UserDefaults.standard.bool(forKey: "healthkit.permissions.requested")
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "healthkit.permissions.requested")
        }
    }

    // MARK: - Authorization

    func requestAuthorization() async throws {
        guard isAvailable else { return }
        let readTypes = Set(Self.allTypes.compactMap { $0.objectType })
        try await store.requestAuthorization(toShare: [], read: readTypes)
        hasRequestedPermissionsBefore = true
    }

    // MARK: - Snapshot creation

    @MainActor
    func createSnapshot(
        in context: ModelContext,
        selectedTypeIDs: Set<String>,
        adaptiveTimeoutsEnabled: Bool,
        triggerReason: String = "manual",
        retryOfSnapshotID: UUID? = nil,
        timeoutMultiplier: Double = 1,
        reviewAmbiguousCompleteDisappearedTypes: Bool = false,
        progress: SnapshotFetchProgress? = nil
    ) async throws -> HealthSnapshot {
        let active = Self.allTypes
            .filter { selectedTypeIDs.contains($0.id) }
            .sortedByFetchDisplayNameDescending()
        let deviceResolution = KeychainService.resolveDeviceID(swiftDataStoreIsEmpty: isStoreEmpty(context: context))

        let snapshot = HealthSnapshot(
            timestamp: Date(),
            osVersion: ProcessInfo.processInfo.operatingSystemVersionString,
            deviceName: UIDevice.current.name,
            deviceID: deviceResolution.id
        )
        let intendedTypeIDs = active.map { $0.id }
        let monitoredTypeSetHash = HashService.typeSetHash(typeIDs: intendedTypeIDs)
        let archiveObservationID = try await archiveStore.beginObservation(
            observedAt: snapshot.timestamp,
            triggerReason: triggerReason,
            selectedTypeSetHash: monitoredTypeSetHash
        )
        snapshot.archiveObservationID = archiveObservationID
        snapshot.monitoredTypeSetHash = monitoredTypeSetHash
        snapshot.recoveredDeviceID = deviceResolution.isRecovered
        snapshot.triggerReason = triggerReason
        snapshot.retryOfSnapshotID = retryOfSnapshotID
        snapshot.yearlyCountTimezoneIdentifier = TimeZone.current.identifier
        let previousSnapshot = findPreviousSnapshot(deviceID: snapshot.deviceID, excluding: snapshot.id, context: context)
        // Fetch raw HealthKit data one type at a time, then assemble each SwiftData model
        // immediately so large per-type archives are not duplicated in an intermediate result array.
        let typeCounts = await fetchAllTypeCounts(
            for: active,
            context: context,
            snapshot: snapshot,
            previousSnapshot: previousSnapshot,
            archiveObservationID: archiveObservationID,
            adaptiveTimeoutsEnabled: adaptiveTimeoutsEnabled,
            timeoutMultiplier: timeoutMultiplier,
            progress: progress
        )
        snapshot.typeCounts = typeCounts

        applyStickyUnavailableState(
            snapshot: snapshot,
            typeCounts: typeCounts,
            context: context
        )

        // Invariant assertions before save — debug asserts + release silent correction
        for tc in typeCounts {
            let isComplete = tc.quality == SnapshotQuality.complete
            assert(
                !isComplete || tc.count >= 0,
                "TypeCount with quality .complete must have count >= 0"
            )
            assert(
                isComplete || tc.count == -1,
                "TypeCount with quality != .complete must have count == -1"
            )
            if !isComplete && tc.count != -1 {
                logger.critical("TypeCount invariant violation: quality=\(tc.quality.rawValue) count=\(tc.count) type=\(tc.typeIdentifier)")
                tc.count = -1
            }
        }

        snapshot.snapshotQuality = deriveSnapshotQuality(from: typeCounts)
        updateSnapshotSummaryCache(snapshot: snapshot, typeCounts: typeCounts)

        configureSnapshotMetadata(
            snapshot,
            typeCounts: typeCounts,
            intendedTypeIDs: intendedTypeIDs,
            context: context
        )
        markContentEquivalenceIfNeeded(
            snapshot: snapshot,
            typeCounts: typeCounts,
            context: context
        )
        precomputeTypeCountDetailCaches(
            snapshot: snapshot,
            typeCounts: typeCounts,
            context: context
        )

        if snapshot.snapshotQuality == .complete,
           reviewAmbiguousCompleteDisappearedTypes,
           hasAmbiguousCompleteDisappearance(snapshot: snapshot, typeCounts: typeCounts, context: context) {
            try await archiveStore.finishObservation(
                observationID: archiveObservationID,
                status: "needs_review",
                endedAt: Date()
            )
            return snapshot
        }

        do {
            if snapshot.snapshotQuality == .complete {
                try await persistSnapshot(snapshot, typeCounts: typeCounts, context: context)
            }
            try await archiveStore.finishObservation(
                observationID: archiveObservationID,
                status: snapshot.snapshotQuality == .complete ? "completed" : "partial_\(snapshot.snapshotQuality.rawValue)",
                endedAt: Date()
            )
        } catch {
            try? await archiveStore.finishObservation(
                observationID: archiveObservationID,
                status: "failed",
                endedAt: Date()
            )
            throw error
        }

        return snapshot
    }

    @MainActor
    func savePartialSnapshot(_ snapshot: HealthSnapshot, in context: ModelContext) async throws -> HealthSnapshot {
        let typeCounts = snapshot.typeCounts ?? []
        guard snapshot.snapshotQuality != .complete else {
            return snapshot
        }

        updateSnapshotSummaryCache(snapshot: snapshot, typeCounts: typeCounts)

        configureSnapshotMetadata(
            snapshot,
            typeCounts: typeCounts,
            intendedTypeIDs: typeCounts.map(\.typeIdentifier),
            context: context
        )
        markContentEquivalenceIfNeeded(
            snapshot: snapshot,
            typeCounts: typeCounts,
            context: context
        )
        precomputeTypeCountDetailCaches(
            snapshot: snapshot,
            typeCounts: typeCounts,
            context: context
        )
        try await persistSnapshot(snapshot, typeCounts: typeCounts, context: context)
        return snapshot
    }

    @MainActor
    func saveReviewedCompleteSnapshot(_ snapshot: HealthSnapshot, in context: ModelContext) async throws -> HealthSnapshot {
        let typeCounts = snapshot.typeCounts ?? []
        snapshot.snapshotQuality = deriveSnapshotQuality(from: typeCounts)
        guard snapshot.snapshotQuality == .complete else {
            return try await savePartialSnapshot(snapshot, in: context)
        }

        updateSnapshotSummaryCache(snapshot: snapshot, typeCounts: typeCounts)

        configureSnapshotMetadata(
            snapshot,
            typeCounts: typeCounts,
            intendedTypeIDs: typeCounts.map(\.typeIdentifier),
            context: context
        )
        markContentEquivalenceIfNeeded(
            snapshot: snapshot,
            typeCounts: typeCounts,
            context: context
        )
        precomputeTypeCountDetailCaches(
            snapshot: snapshot,
            typeCounts: typeCounts,
            context: context
        )
        try await persistSnapshot(snapshot, typeCounts: typeCounts, context: context)
        return snapshot
    }

    // MARK: - Snapshot persistence

    private func persistSnapshot(
        _ snapshot: HealthSnapshot,
        typeCounts: [TypeCount],
        context: ModelContext
    ) async throws {
        context.insert(snapshot)
        for typeCount in typeCounts {
            context.insert(typeCount)
            for yearlyCount in typeCount.yearlyCounts ?? [] {
                context.insert(yearlyCount)
            }
            for bin in typeCount.distributionBins ?? [] {
                context.insert(bin)
            }
            typeCount.snapshot = snapshot
        }
        snapshot.typeCounts = typeCounts

        try context.save()

        try await runPostSavePipeline(snapshot: snapshot, context: context)
    }

    private func updateSnapshotSummaryCache(
        snapshot: HealthSnapshot,
        typeCounts: [TypeCount]
    ) {
        snapshot.cachedSummaryVersion = HealthSnapshot.currentCachedSummaryVersion
        snapshot.cachedTypeCount = typeCounts.count
        snapshot.cachedRecordCount = typeCounts.reduce(0) { partial, typeCount in
            typeCount.count > 0 ? partial + typeCount.count : partial
        }

        let datedTypeCounts = typeCounts.filter { !$0.isUnsupported && $0.count > 0 }
        snapshot.cachedEarliestRecordDate = datedTypeCounts.compactMap(\.earliestDate).min()
        snapshot.cachedLatestRecordDate = datedTypeCounts.compactMap(\.latestDate).max()
    }

    private func configureSnapshotMetadata(
        _ snapshot: HealthSnapshot,
        typeCounts: [TypeCount],
        intendedTypeIDs: [String],
        context: ModelContext
    ) {
        // Chain metadata — set BEFORE context.save()
        // localSequenceNumber is used here solely to find the latest local candidate during
        // snapshot creation. Once previousSnapshotID is set, all chain reconstruction must use
        // previousSnapshotID exclusively — never reconstruct a chain by walking localSequenceNumber.
        let previous = findPreviousSnapshot(deviceID: snapshot.deviceID, excluding: snapshot.id, context: context)
        if let previous {
            snapshot.previousSnapshotID = previous.id
            snapshot.localSequenceNumber = previous.localSequenceNumber + 1
            snapshot.isChainStart = false

            snapshot.monitoredTypeSetHash = HashService.typeSetHash(typeIDs: intendedTypeIDs)
            if snapshot.monitoredTypeSetHash != previous.monitoredTypeSetHash {
                snapshot.monitoredRegistryVersion = previous.monitoredRegistryVersion + 1
            } else {
                snapshot.monitoredRegistryVersion = previous.monitoredRegistryVersion
            }

            // Auto-detect post-restore: previous was fully unauthorized, current is complete
            if previous.snapshotQuality == SnapshotQuality.unauthorized && snapshot.snapshotQuality == SnapshotQuality.complete {
                snapshot.isPostRestore = true
                snapshot.isPostRestoreInferred = true
            }
        } else {
            snapshot.previousSnapshotID = nil
            snapshot.localSequenceNumber = 0
            snapshot.isChainStart = true
            snapshot.monitoredTypeSetHash = HashService.typeSetHash(typeIDs: intendedTypeIDs)
            snapshot.monitoredRegistryVersion = 0

            // Auto-detect post-restore on chain start with significant data
            let completeTypeCounts = typeCounts.filter { $0.quality == SnapshotQuality.complete }
            let completeCount = completeTypeCounts.reduce(0) { $0 + max($1.count, 0) }
            if completeCount > 1000 {
                snapshot.isPostRestore = true
                snapshot.isPostRestoreInferred = true
            }
        }

        // Device metadata — informational only, never used for chain linkage
        snapshot.hardwareModel = hardwareModel()
        snapshot.appBuildVersion = appBuildVersion()
    }

    private func applyStickyUnavailableState(
        snapshot: HealthSnapshot,
        typeCounts: [TypeCount],
        context: ModelContext
    ) {
        guard let previous = findPreviousSnapshot(deviceID: snapshot.deviceID, excluding: snapshot.id, context: context) else {
            return
        }

        let previousByType = Dictionary(
            uniqueKeysWithValues: (previous.typeCounts ?? []).map { ($0.typeIdentifier, $0) }
        )

        for current in typeCounts {
            guard current.quality == .complete,
                  current.count == 0,
                  let previousType = previousByType[current.typeIdentifier],
                  previousType.quality == .unauthorized else {
                continue
            }

            current.count = -1
            current.contentHash = ""
            current.earliestDate = nil
            current.latestDate = nil
            current.quality = .unauthorized
            current.yearlyCounts?.removeAll()
        }
    }

    private func precomputeTypeCountDetailCaches(
        snapshot: HealthSnapshot,
        typeCounts: [TypeCount],
        context: ModelContext
    ) {
        MemoryLog.log("healthKit.precomputeDetailCaches.begin", metadata: [
            "typeCountCount": "\(typeCounts.count)",
            "hasPrevious": "\(snapshot.previousSnapshotID != nil)"
        ])

        guard let previousID = snapshot.previousSnapshotID,
              let previous = fetchSnapshot(id: previousID, context: context) else {
            for typeCount in typeCounts {
                typeCount.setDetailCache(nil)
            }
            MemoryLog.log("healthKit.precomputeDetailCaches.noPrevious", metadata: [
                "typeCountCount": "\(typeCounts.count)"
            ])
            return
        }

        let previousByType = Dictionary(
            uniqueKeysWithValues: (previous.typeCounts ?? []).map { ($0.typeIdentifier, $0) }
        )
        var builtCount = 0
        var skippedAliasCount = 0

        for typeCount in typeCounts {
            if typeCount.isContentAlias {
                typeCount.setDetailCache(nil)
                skippedAliasCount += 1
                continue
            }

            MemoryLog.log("healthKit.detailCache.buildBegin", metadata: detailCacheMetadata(
                current: typeCount,
                previous: previousByType[typeCount.typeIdentifier],
                source: "snapshotSave"
            ))
            typeCount.setDetailCache(
                TypeCountDetailCacheBuilder.build(
                    current: typeCount,
                    previous: previousByType[typeCount.typeIdentifier],
                    baselineSnapshotID: previous.id
                )
            )
            builtCount += 1
            MemoryLog.log("healthKit.detailCache.buildEnd", metadata: detailCacheMetadata(
                current: typeCount,
                previous: previousByType[typeCount.typeIdentifier],
                source: "snapshotSave"
            ))
        }
        MemoryLog.log("healthKit.precomputeDetailCaches.end", metadata: [
            "builtCount": "\(builtCount)",
            "skippedAliasCount": "\(skippedAliasCount)"
        ])
    }

    private func markContentEquivalenceIfNeeded(
        snapshot: HealthSnapshot,
        typeCounts: [TypeCount],
        context: ModelContext
    ) {
        snapshot.contentEquivalentSnapshotID = nil
        for typeCount in typeCounts {
            typeCount.contentEquivalentTypeCountID = nil
        }

        guard let previousID = snapshot.previousSnapshotID,
              let previous = fetchSnapshot(id: previousID, context: context),
              previous.monitoredTypeSetHash == snapshot.monitoredTypeSetHash else {
            return
        }

        let previousByType = Dictionary(
            uniqueKeysWithValues: (previous.typeCounts ?? []).map { ($0.typeIdentifier, $0) }
        )

        for typeCount in typeCounts {
            guard let previousType = previousByType[typeCount.typeIdentifier],
                  areTypeCountsContentEquivalent(typeCount, previousType) else {
                continue
            }

            typeCount.contentEquivalentTypeCountID = previousType.contentRepresentativeTypeCountID
        }

        if areTypeCountsContentEquivalent(previous.typeCounts ?? [], typeCounts) {
            snapshot.contentEquivalentSnapshotID = previous.contentRepresentativeSnapshotID
        }
    }

    private func areTypeCountsContentEquivalent(_ lhs: [TypeCount], _ rhs: [TypeCount]) -> Bool {
        let lhsByType = Dictionary(uniqueKeysWithValues: lhs.map { ($0.typeIdentifier, $0) })
        let rhsByType = Dictionary(uniqueKeysWithValues: rhs.map { ($0.typeIdentifier, $0) })
        guard lhsByType.keys == rhsByType.keys else { return false }

        for typeIdentifier in lhsByType.keys {
            guard let lhsType = lhsByType[typeIdentifier],
                  let rhsType = rhsByType[typeIdentifier],
                  lhsType.count == rhsType.count,
                  lhsType.contentHash == rhsType.contentHash,
                  lhsType.quality == rhsType.quality,
                  lhsType.isUnsupported == rhsType.isUnsupported else {
                return false
            }
        }

        return true
    }

    private func areTypeCountsContentEquivalent(_ lhs: TypeCount, _ rhs: TypeCount) -> Bool {
        lhs.count == rhs.count &&
        lhs.contentHash == rhs.contentHash &&
        lhs.quality == rhs.quality &&
        lhs.isUnsupported == rhs.isUnsupported
    }

    private func detailCacheMetadata(current: TypeCount, previous: TypeCount?, source: String) -> [String: String] {
        [
            "source": source,
            "type": current.typeIdentifier,
            "currentCount": "\(current.count)",
            "previousCount": "\(previous?.count ?? 0)",
            "currentArchive": current.recordArchiveData.map { MemoryLog.format(UInt64($0.count)) } ?? "nil",
            "previousArchive": previous?.recordArchiveData.map { MemoryLog.format(UInt64($0.count)) } ?? "nil",
            "isAlias": "\(current.isContentAlias)"
        ]
    }

    private func hasAmbiguousCompleteDisappearance(
        snapshot: HealthSnapshot,
        typeCounts: [TypeCount],
        context: ModelContext
    ) -> Bool {
        guard let previousID = snapshot.previousSnapshotID,
              let previous = fetchSnapshot(id: previousID, context: context) else {
            return false
        }

        let previousByType = Dictionary(
            uniqueKeysWithValues: (previous.typeCounts ?? []).map { ($0.typeIdentifier, $0) }
        )

        return typeCounts.contains { current in
            guard current.quality == .complete,
                  current.count == 0,
                  let previous = previousByType[current.typeIdentifier],
                  previous.quality == .complete,
                  previous.count > 0 else {
                return false
            }
            return true
        }
    }

    // MARK: - Post-save pipeline

    private func runPostSavePipeline(
        snapshot: HealthSnapshot,
        context: ModelContext
    ) async throws {
        guard snapshot.previousSnapshotID != nil else { return }
        _ = try DeltaService.computeAndSave(current: snapshot, context: context)
    }

    // MARK: - Per-type fetch pipeline

    // Fetches sequentially to prevent race conditions and resource exhaustion.
    @MainActor
    private func fetchAllTypeCounts(
        for active: [MonitoredType],
        context: ModelContext,
        snapshot: HealthSnapshot,
        previousSnapshot: HealthSnapshot?,
        archiveObservationID: Int64,
        adaptiveTimeoutsEnabled: Bool,
        timeoutMultiplier: Double,
        progress: SnapshotFetchProgress? = nil
    ) async -> [TypeCount] {
        var typeCounts: [TypeCount] = []
        typeCounts.reserveCapacity(active.count)

        for monitoredType in active {
            var profile = timeoutProfile(for: monitoredType)
            let timeout = timeoutForFetch(
                profile: profile,
                adaptiveTimeoutsEnabled: adaptiveTimeoutsEnabled,
                timeoutMultiplier: timeoutMultiplier
            )
            var result = await fetchTypeCountData(
                for: monitoredType,
                timeoutProfile: profile,
                timeoutSeconds: timeout,
                previousTypeCount: previousSnapshot?.typeCounts?.first { $0.typeIdentifier == monitoredType.id },
                archiveObservationID: archiveObservationID,
                progress: progress
            )
            updateTimeoutProfile(&profile, with: result, monitoredType: monitoredType)
            LocalMetricTimeoutProfileStore.save(profile)
            result.applyTimeoutProfile(profile)
            progress?.updateTimeoutProfile(from: profile, for: monitoredType.id)
            let typeCount = result.makeTypeCount()
            typeCount.snapshot = snapshot
            typeCounts.append(typeCount)
        }
        return typeCounts
    }

    private func fetchTypeCountData(
        for monitoredType: MonitoredType,
        timeoutProfile: LocalMetricTimeoutProfile,
        timeoutSeconds: TimeInterval,
        previousTypeCount: TypeCount?,
        archiveObservationID: Int64,
        progress: SnapshotFetchProgress? = nil
    ) async -> TypeCountFetchResult {
        let started = Date()
        progress?.updateStatus(monitoredType.id, status: .fetching)

        // Unsupported type: HKObjectType factory returned nil for this identifier
        guard let objectType = monitoredType.objectType,
              let sampleType = objectType as? HKSampleType else {
            var result = TypeCountFetchResult(
                typeIdentifier: monitoredType.id,
                displayName: monitoredType.displayName,
                count: -1,
                contentHash: "",
                earliestDate: nil,
                latestDate: nil,
                quality: SnapshotQuality.failed,
                diagnosticQuality: HealthKitAPICallResult.Status.unsupported.rawValue,
                isUnsupported: true,
                authorizationStatus: "unavailable",
                apiCalls: Self.placeholderAPICalls(status: .unsupported, message: "HealthKit type is not available on this OS or device"),
                yearlyCounts: [],
                distributionBins: [],
                records: [],
                recordArchiveData: nil
            )
            result.totalElapsedSeconds = Date().timeIntervalSince(started)
            result.timeoutConfiguredSeconds = timeoutSeconds
            result.applyTimeoutProfile(timeoutProfile)
            progress?.updateDetails(from: result)
            progress?.updateStatus(monitoredType.id, status: .failed("Unsupported"))
            return result
        }

        var result = await fetchTypeCountDataFromHK(
            monitoredType: monitoredType,
            sampleType: sampleType,
            timeoutSeconds: timeoutSeconds,
            previousTypeCount: previousTypeCount,
            archiveObservationID: archiveObservationID,
            progress: progress
        )
        result.totalElapsedSeconds = Date().timeIntervalSince(started)
        result.timeoutConfiguredSeconds = timeoutSeconds
        result.applyTimeoutProfile(timeoutProfile)
        progress?.updateDetails(from: result)

        if result.diagnosticQuality == SnapshotQuality.complete.rawValue {
            progress?.updateStatus(monitoredType.id, status: .complete, recordCount: max(result.count, 0))
        } else if result.apiCalls.contains(where: { $0.status == .timeout }) {
            progress?.updateStatus(monitoredType.id, status: .failed("Timeout"))
        } else if result.diagnosticQuality == SnapshotQuality.unauthorized.rawValue {
            progress?.updateStatus(monitoredType.id, status: .failed("Not authorized"))
        } else {
            progress?.updateStatus(monitoredType.id, status: .failed("Failed"))
        }

        return result
    }

    private func fetchTypeCountDataFromHK(
        monitoredType: MonitoredType,
        sampleType: HKSampleType,
        timeoutSeconds: TimeInterval,
        previousTypeCount: TypeCount?,
        archiveObservationID: Int64,
        progress: SnapshotFetchProgress?
    ) async -> TypeCountFetchResult {
        let dateDeadline = Date().addingTimeInterval(timeoutSeconds)
        async let earliestTask = measureAPICall(
            queryType: "earliest_sample",
            timeoutSeconds: max(0, dateDeadline.timeIntervalSinceNow)
        ) {
            try await self.fetchEarliestDate(for: sampleType)
        } resultDescription: { date in
            Self.iso8601String(for: date)
        }
        async let latestTask = measureAPICall(
            queryType: "latest_sample",
            timeoutSeconds: max(0, dateDeadline.timeIntervalSinceNow)
        ) {
            try await self.fetchLatestDate(for: sampleType)
        } resultDescription: { date in
            Self.iso8601String(for: date)
        }
        let earliestResult = await earliestTask
        let latestResult = await latestTask
        var apiCalls = [earliestResult.apiCall, latestResult.apiCall]

        guard earliestResult.apiCall.status == .complete, latestResult.apiCall.status == .complete else {
            let status = firstImpairedStatus(in: apiCalls)
            let quality = diagnosticQuality(for: status)
            return TypeCountFetchResult(
                typeIdentifier: monitoredType.id,
                displayName: monitoredType.displayName,
                count: -1,
                contentHash: "",
                earliestDate: earliestResult.value ?? nil,
                latestDate: latestResult.value ?? nil,
                quality: snapshotQuality(for: status),
                diagnosticQuality: quality,
                isUnsupported: false,
                authorizationStatus: authorizationStatus(from: apiCalls),
                apiCalls: apiCalls,
                yearlyCounts: [],
                distributionBins: [],
                records: [],
                recordArchiveData: nil
            )
        }

        let earliest = earliestResult.value ?? nil
        let latest = latestResult.value ?? nil
        let previousDistribution = PreviousDistributionState(typeCount: previousTypeCount)
        let distributionResult = await measureAPICall(
            queryType: "record_import",
            timeoutSeconds: DistributionCaptureConfiguration.fullImportTimeoutSeconds
        ) {
            try await self.fetchDistribution(
                for: sampleType,
                typeIdentifier: monitoredType.id,
                earliestDate: earliest,
                latestDate: latest,
                previousDistribution: previousDistribution,
                archiveObservationID: archiveObservationID,
                progress: progress
            )
        } resultDescription: { distribution in
            "\(distribution.totalCount) samples in \(distribution.bins.count) anchored segment(s)"
        }
        apiCalls.insert(distributionResult.apiCall, at: 0)

        guard distributionResult.apiCall.status == .complete, let distribution = distributionResult.value else {
            let status = distributionResult.apiCall.status
            let quality = diagnosticQuality(for: status)
            return TypeCountFetchResult(
                typeIdentifier: monitoredType.id,
                displayName: monitoredType.displayName,
                count: -1,
                contentHash: "",
                earliestDate: earliest,
                latestDate: latest,
                quality: snapshotQuality(for: status),
                diagnosticQuality: quality,
                isUnsupported: false,
                authorizationStatus: authorizationStatus(from: apiCalls),
                apiCalls: apiCalls,
                yearlyCounts: [],
                distributionBins: [],
                records: [],
                recordArchiveData: nil
            )
        }

        let contentHash = distribution.contentHash ?? HashService.typeHash(
            typeIdentifier: monitoredType.id,
            recordFingerprints: distribution.records.map(\.recordFingerprint)
        )

        // YearlyCount uses Calendar.current; year attribution is local-time based.
        let yearMap: [Int: Int]
        if let cachedYearlyCounts = distribution.yearlyCounts {
            yearMap = cachedYearlyCounts
        } else {
            var computedYearMap: [Int: Int] = [:]
            for record in distribution.records {
                let year = Calendar.current.component(.year, from: record.startDate)
                computedYearMap[year, default: 0] += 1
            }
            yearMap = computedYearMap
        }
        let yearlyCounts = yearMap.map { year, yearCount in
            TypeCountFetchResult.YearlyCountData(
                year: year,
                count: yearCount,
                typeIdentifier: monitoredType.id,
                isApproximate: false
            )
        }
        progress?.updateBlockProgress(
            monitoredType.id,
            detail: "Preparing record archive",
            recordCount: distribution.totalCount
        )
        let recordArchiveData = distribution.recordArchiveData ?? HealthRecordArchive.encode(distribution.records)

        return TypeCountFetchResult(
            typeIdentifier: monitoredType.id,
            displayName: monitoredType.displayName,
            count: distribution.totalCount,
            contentHash: contentHash,
            earliestDate: earliest,
            latestDate: latest,
            quality: SnapshotQuality.complete,
            diagnosticQuality: HealthKitAPICallResult.Status.complete.rawValue,
            isUnsupported: false,
            authorizationStatus: authorizationStatus(from: apiCalls),
            apiCalls: apiCalls,
            yearlyCounts: yearlyCounts,
            distributionBins: distribution.bins.map {
                TypeCountFetchResult.DistributionBinData(
                    bucketStart: $0.start,
                    bucketEnd: $0.end,
                    count: $0.count,
                    contentHash: $0.contentHash,
                    anchorData: $0.anchorData
                )
            },
            records: [],
            recordArchiveData: recordArchiveData
        )
    }

    // MARK: - HealthKit queries

    private func fetchDistribution(
        for sampleType: HKSampleType,
        typeIdentifier: String,
        earliestDate: Date?,
        latestDate: Date?,
        previousDistribution: PreviousDistributionState,
        archiveObservationID: Int64,
        progress: SnapshotFetchProgress?
    ) async throws -> SampleDistribution {
        var anchor = previousDistribution.globalAnchor
        let startedFromAnchor = anchor != nil
        let estimatedPageCount = startedFromAnchor
            ? Self.estimatedPageCount(for: previousDistribution.count)
            : nil
        let progressStarted = Date()
        var pageNumber = 0
        var processedEventCount = 0
        var firstDeltaPage: SampleDistributionPage?

        if anchor != nil {
            pageNumber = 1
            progress?.updateBlockProgress(
                typeIdentifier,
                detail: Self.pageProgressDetail(
                    operation: "Delta",
                    pageNumber: pageNumber,
                    estimatedPageCount: estimatedPageCount
                ),
                recordCount: previousDistribution.count,
                elapsedSeconds: 0,
                samplesPerSecond: 0
            )

            let page = try await withTimeout(seconds: DistributionCaptureConfiguration.pageTimeoutSeconds) {
                try await self.fetchDistributionPage(
                    for: sampleType,
                    predicate: nil,
                    anchor: anchor
                )
            }
            try await archivePage(page, sampleType: sampleType, observationID: archiveObservationID)
            anchor = page.anchor

            if page.samples.isEmpty, page.deletedObjects.isEmpty,
               let unchanged = previousDistribution.unchangedDistribution(
                    updatedAnchor: anchor,
                    typeIdentifier: typeIdentifier,
                    earliestDate: earliestDate,
                    latestDate: latestDate
               ) {
                try await archiveStore.markVerification(
                    sampleType: sampleType,
                    verifiedAt: Date(),
                    observationID: archiveObservationID
                )
                progress?.updateBlockProgress(
                    typeIdentifier,
                    detail: "No HealthKit delta",
                    recordCount: unchanged.totalCount,
                    elapsedSeconds: Date().timeIntervalSince(progressStarted),
                    samplesPerSecond: 0
                )
                return unchanged
            }

            firstDeltaPage = page
        }

        if !startedFromAnchor {
            return try await fetchInitialDistributionStreaming(
                for: sampleType,
                typeIdentifier: typeIdentifier,
                earliestDate: earliestDate,
                latestDate: latestDate,
                progressStarted: progressStarted,
                archiveObservationID: archiveObservationID,
                progress: progress
            )
        }

        var recordMap = startedFromAnchor ? try previousDistribution.makeRecordMap() : [:]
        var shouldFetchNextPage = true

        if let firstDeltaPage {
            applyDistributionPage(firstDeltaPage, sampleType: sampleType, to: &recordMap)
            processedEventCount += pageEventCount(firstDeltaPage)
            shouldFetchNextPage = firstDeltaPage.samples.count + firstDeltaPage.deletedObjects.count >= DistributionCaptureConfiguration.queryPageLimit
            progress?.updateBlockProgress(
                typeIdentifier,
                detail: Self.pageProgressDetail(
                    operation: "Delta",
                    pageNumber: pageNumber,
                    estimatedPageCount: estimatedPageCount
                ),
                recordCount: recordMap.count,
                elapsedSeconds: Date().timeIntervalSince(progressStarted),
                samplesPerSecond: Self.samplesPerSecond(
                    processedCount: processedEventCount,
                    elapsedSeconds: Date().timeIntervalSince(progressStarted)
                )
            )
        }

        while shouldFetchNextPage {
            pageNumber += 1
            progress?.updateBlockProgress(
                typeIdentifier,
                detail: Self.pageProgressDetail(
                    operation: anchor == nil ? "Import" : "Delta",
                    pageNumber: pageNumber,
                    estimatedPageCount: anchor == nil ? nil : estimatedPageCount
                ),
                recordCount: recordMap.count,
                elapsedSeconds: Date().timeIntervalSince(progressStarted),
                samplesPerSecond: Self.samplesPerSecond(
                    processedCount: processedEventCount,
                    elapsedSeconds: Date().timeIntervalSince(progressStarted)
                )
            )

            let page = try await withTimeout(seconds: DistributionCaptureConfiguration.pageTimeoutSeconds) {
                try await self.fetchDistributionPage(
                    for: sampleType,
                    predicate: nil,
                    anchor: anchor
                )
            }
            try await archivePage(page, sampleType: sampleType, observationID: archiveObservationID)
            anchor = page.anchor

            applyDistributionPage(page, sampleType: sampleType, to: &recordMap)
            processedEventCount += pageEventCount(page)
            shouldFetchNextPage = page.samples.count + page.deletedObjects.count >= DistributionCaptureConfiguration.queryPageLimit
            progress?.updateBlockProgress(
                typeIdentifier,
                detail: Self.pageProgressDetail(
                    operation: anchor == nil ? "Import" : "Delta",
                    pageNumber: pageNumber,
                    estimatedPageCount: anchor == nil ? nil : estimatedPageCount
                ),
                recordCount: recordMap.count,
                elapsedSeconds: Date().timeIntervalSince(progressStarted),
                samplesPerSecond: Self.samplesPerSecond(
                    processedCount: processedEventCount,
                    elapsedSeconds: Date().timeIntervalSince(progressStarted)
                )
            )
        }

        try await archiveStore.markVerification(sampleType: sampleType, verifiedAt: Date(), observationID: archiveObservationID)

        let sortedKeys = recordMap.keys.sorted {
            guard let left = recordMap[$0],
                  let right = recordMap[$1] else {
                return $0 < $1
            }
            if left.startDate != right.startDate {
                return left.startDate < right.startDate
            }
            return left.recordFingerprint < right.recordFingerprint
        }
        var sortedRecords: [HealthRecordValue] = []
        sortedRecords.reserveCapacity(sortedKeys.count)
        for sampleUUIDHash in sortedKeys {
            guard let record = recordMap[sampleUUIDHash] else { continue }
            sortedRecords.append(
                HealthRecordValue(
                    typeIdentifier: typeIdentifier,
                    sampleUUIDHash: sampleUUIDHash,
                    recordFingerprint: record.recordFingerprint,
                    startDate: record.startDate,
                    endDate: record.endDate,
                    displayValue: record.displayValue
                )
            )
        }
        let contentHash = HashService.typeHash(
            typeIdentifier: typeIdentifier,
            recordFingerprints: sortedRecords.map(\.recordFingerprint)
        )

        progress?.updateBlockProgress(
            typeIdentifier,
            detail: pageNumber == 1 ? "Imported 1 page" : "Imported \(pageNumber) pages",
            recordCount: sortedRecords.count
        )

        guard !sortedRecords.isEmpty || anchor != nil else {
            return SampleDistribution(
                totalCount: 0,
                bins: [],
                records: [],
                contentHash: nil,
                yearlyCounts: nil,
                recordArchiveData: nil
            )
        }

        let binStart = earliestDate ?? sortedRecords.first?.startDate ?? previousDistribution.earliestRecordDate ?? Date()
        let rawBinEnd = latestDate ?? sortedRecords.last?.endDate ?? previousDistribution.latestRecordDate ?? binStart
        let binEnd = rawBinEnd > binStart ? rawBinEnd : binStart.addingTimeInterval(1)

        return SampleDistribution(
            totalCount: sortedRecords.count,
            bins: [
                SampleDistribution.Bin(
                    start: binStart,
                    end: binEnd,
                    count: sortedRecords.count,
                    contentHash: contentHash,
                    anchorData: anchor.flatMap(Self.archiveAnchor(_:))
                )
            ],
            records: sortedRecords,
            contentHash: contentHash,
            yearlyCounts: nil,
            recordArchiveData: nil
        )
    }

    private func fetchInitialDistributionStreaming(
        for sampleType: HKSampleType,
        typeIdentifier: String,
        earliestDate: Date?,
        latestDate: Date?,
        progressStarted: Date,
        archiveObservationID: Int64,
        progress: SnapshotFetchProgress?
    ) async throws -> SampleDistribution {
        var anchor: HKQueryAnchor?
        var pageNumber = 0
        var recordCount = 0
        var processedEventCount = 0
        var firstRecordDate: Date?
        var latestRecordDate: Date?
        var yearMap: [Int: Int] = [:]
        var archiveWriter = HealthRecordArchive.makeCompactWriter(typeIdentifier: typeIdentifier)
        var hashBuilder = HashService.TypeHashBuilder(typeIdentifier: typeIdentifier)
        var shouldFetchNextPage = true

        while shouldFetchNextPage {
            pageNumber += 1
            let elapsedBeforePage = Date().timeIntervalSince(progressStarted)
            progress?.updateBlockProgress(
                typeIdentifier,
                detail: Self.pageProgressDetail(
                    operation: "Import",
                    pageNumber: pageNumber,
                    estimatedPageCount: nil
                ),
                recordCount: recordCount,
                elapsedSeconds: elapsedBeforePage,
                samplesPerSecond: Self.samplesPerSecond(
                    processedCount: processedEventCount,
                    elapsedSeconds: elapsedBeforePage
                )
            )

            let page = try await withTimeout(seconds: DistributionCaptureConfiguration.pageTimeoutSeconds) {
                try await self.fetchDistributionPage(
                    for: sampleType,
                    predicate: nil,
                    anchor: anchor
                )
            }
            try await archivePage(page, sampleType: sampleType, observationID: archiveObservationID)
            anchor = page.anchor

            for sample in page.samples {
                let value = Self.recordValue(for: sample, sampleType: sampleType, typeIdentifier: typeIdentifier)
                archiveWriter.append(value)
                hashBuilder.append(recordFingerprint: value.recordFingerprint)
                yearMap[Calendar.current.component(.year, from: value.startDate), default: 0] += 1
                firstRecordDate = min(firstRecordDate ?? value.startDate, value.startDate)
                latestRecordDate = max(latestRecordDate ?? value.endDate, value.endDate)
                recordCount += 1
            }

            processedEventCount += pageEventCount(page)
            shouldFetchNextPage = page.samples.count + page.deletedObjects.count >= DistributionCaptureConfiguration.queryPageLimit
            let elapsedAfterPage = Date().timeIntervalSince(progressStarted)
            progress?.updateBlockProgress(
                typeIdentifier,
                detail: Self.pageProgressDetail(
                    operation: "Import",
                    pageNumber: pageNumber,
                    estimatedPageCount: nil
                ),
                recordCount: recordCount,
                elapsedSeconds: elapsedAfterPage,
                samplesPerSecond: Self.samplesPerSecond(
                    processedCount: processedEventCount,
                    elapsedSeconds: elapsedAfterPage
                )
            )
        }

        let contentHash = hashBuilder.finalize()
        progress?.updateBlockProgress(
            typeIdentifier,
            detail: pageNumber == 1 ? "Imported 1 page" : "Imported \(pageNumber) pages",
            recordCount: recordCount,
            elapsedSeconds: Date().timeIntervalSince(progressStarted),
            samplesPerSecond: Self.samplesPerSecond(
                processedCount: processedEventCount,
                elapsedSeconds: Date().timeIntervalSince(progressStarted)
            )
        )

        try await archiveStore.markVerification(sampleType: sampleType, verifiedAt: Date(), observationID: archiveObservationID)

        guard recordCount > 0 || anchor != nil else {
            return SampleDistribution(
                totalCount: 0,
                bins: [],
                records: [],
                contentHash: nil,
                yearlyCounts: nil,
                recordArchiveData: nil
            )
        }

        let binStart = earliestDate ?? firstRecordDate ?? Date()
        let rawBinEnd = latestDate ?? latestRecordDate ?? binStart
        let binEnd = rawBinEnd > binStart ? rawBinEnd : binStart.addingTimeInterval(1)

        return SampleDistribution(
            totalCount: recordCount,
            bins: [
                SampleDistribution.Bin(
                    start: binStart,
                    end: binEnd,
                    count: recordCount,
                    contentHash: contentHash,
                    anchorData: anchor.flatMap(Self.archiveAnchor(_:))
                )
            ],
            records: [],
            contentHash: contentHash,
            yearlyCounts: yearMap,
            recordArchiveData: archiveWriter.finalize()
        )
    }

    private func applyDistributionPage(
        _ page: SampleDistributionPage,
        sampleType: HKSampleType,
        to recordMap: inout [String: SampleRecordPayload]
    ) {
        for deletedObject in page.deletedObjects {
            recordMap.removeValue(forKey: HashService.sampleUUIDHash(deletedObject.uuid.uuidString))
        }

        for sample in page.samples {
            let value = Self.recordValue(for: sample, sampleType: sampleType, typeIdentifier: sampleType.identifier)
            let uuidHash = value.sampleUUIDHash
            recordMap[uuidHash] = SampleRecordPayload(
                recordFingerprint: value.recordFingerprint,
                startDate: value.startDate,
                endDate: value.endDate,
                displayValue: value.displayValue
            )
        }
    }

    private static func recordValue(
        for sample: HKSample,
        sampleType: HKSampleType,
        typeIdentifier: String
    ) -> HealthRecordValue {
        HealthRecordValue(
            typeIdentifier: typeIdentifier,
            sampleUUIDHash: HashService.sampleUUIDHash(sample.uuid.uuidString),
            recordFingerprint: HashService.sampleFingerprint(
                typeIdentifier: sampleType.identifier,
                sampleUUID: sample.uuid.uuidString,
                startDate: sample.startDate,
                endDate: sample.endDate
            ),
            startDate: sample.startDate,
            endDate: sample.endDate,
            displayValue: nil
        )
    }

    private func pageEventCount(_ page: SampleDistributionPage) -> Int {
        page.samples.count + page.deletedObjects.count
    }

    private static func estimatedPageCount(for recordCount: Int) -> Int {
        let count = max(recordCount, 0)
        let pageLimit = DistributionCaptureConfiguration.queryPageLimit
        return max(1, (count + pageLimit - 1) / pageLimit)
    }

    private static func samplesPerSecond(processedCount: Int, elapsedSeconds: TimeInterval) -> Double {
        guard elapsedSeconds > 0, processedCount > 0 else { return 0 }
        return Double(processedCount) / elapsedSeconds
    }

    private static func pageProgressDetail(
        operation: String,
        pageNumber: Int,
        estimatedPageCount: Int?
    ) -> String {
        guard let estimatedPageCount else {
            return "\(operation) page \(pageNumber) (total unknown)"
        }

        if pageNumber <= estimatedPageCount {
            return "\(operation) page \(pageNumber) of ~\(estimatedPageCount)"
        }

        return "\(operation) page \(pageNumber) of ~\(estimatedPageCount)+"
    }

    private func fetchDistributionPage(
        for sampleType: HKSampleType,
        predicate: NSPredicate?,
        anchor: HKQueryAnchor?
    ) async throws -> SampleDistributionPage {
        let box = HealthKitQueryContinuationBox<SampleDistributionPage>()
        nonisolated(unsafe) let queryPredicate = predicate
        return try await withTaskCancellationHandler {
            try await withCheckedThrowingContinuation { continuation in
                box.setContinuation(continuation)
                let query = HKAnchoredObjectQuery(
                    type: sampleType,
                    predicate: queryPredicate,
                    anchor: anchor,
                    limit: DistributionCaptureConfiguration.queryPageLimit
                ) { _, samples, deletedObjects, newAnchor, error in
                    if let error {
                        box.resume(throwing: error)
                        return
                    }

                    box.resume(
                        returning: SampleDistributionPage(
                            samples: samples ?? [],
                            deletedObjects: deletedObjects ?? [],
                            anchor: newAnchor
                        )
                    )
                }
                box.setQuery(query, store: store)
                store.execute(query)
            }
        } onCancel: {
            box.cancel()
        }
    }

    private func archivePage(_ page: SampleDistributionPage, sampleType: HKSampleType, observationID: Int64) async throws {
        let observedAt = Date()
        _ = try await archiveStore.upsertSamples(page.samples, observedAt: observedAt, observationID: observationID)
        for deletedObject in page.deletedObjects {
            try await archiveStore.recordDisappearance(
                sampleUUIDHash: HashService.sampleUUIDHash(deletedObject.uuid.uuidString),
                sampleTypeIdentifier: sampleType.identifier,
                observedMissingAt: observedAt,
                observationID: observationID
            )
        }
    }

    private static func archiveAnchor(_ anchor: HKQueryAnchor) -> Data? {
        try? NSKeyedArchiver.archivedData(withRootObject: anchor, requiringSecureCoding: true)
    }

    private static func unarchiveAnchor(_ data: Data?) -> HKQueryAnchor? {
        guard let data else { return nil }
        return try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: data)
    }

    private static func displayValue(for sample: HKSample) -> String? {
        if let quantitySample = sample as? HKQuantitySample {
            return quantityDisplayValue(for: quantitySample)
        }

        if let categorySample = sample as? HKCategorySample {
            return categoryDisplayValue(for: categorySample)
        }

        if let workout = sample as? HKWorkout {
            return workoutDisplayValue(for: workout)
        }

        return nil
    }

    private static func quantityDisplayValue(for sample: HKQuantitySample) -> String {
        let identifier = sample.quantityType.identifier
        switch identifier {
        case HKQuantityTypeIdentifier.stepCount.rawValue:
            return format(sample.quantity.doubleValue(for: .count()), unit: "")
        case HKQuantityTypeIdentifier.distanceWalkingRunning.rawValue:
            let meters = sample.quantity.doubleValue(for: .meter())
            return meters >= 1_000
                ? "\(format(meters / 1_000, maximumFractionDigits: 2)) km"
                : "\(format(meters, maximumFractionDigits: 0)) m"
        case HKQuantityTypeIdentifier.activeEnergyBurned.rawValue:
            return "\(format(sample.quantity.doubleValue(for: .kilocalorie()), maximumFractionDigits: 0)) kcal"
        case HKQuantityTypeIdentifier.appleExerciseTime.rawValue:
            return "\(format(sample.quantity.doubleValue(for: .minute()), maximumFractionDigits: 0)) min"
        case HKQuantityTypeIdentifier.heartRate.rawValue,
             HKQuantityTypeIdentifier.restingHeartRate.rawValue:
            return "\(format(sample.quantity.doubleValue(for: HKUnit.count().unitDivided(by: .minute())), maximumFractionDigits: 0)) bpm"
        case HKQuantityTypeIdentifier.respiratoryRate.rawValue:
            return "\(format(sample.quantity.doubleValue(for: HKUnit.count().unitDivided(by: .minute())), maximumFractionDigits: 1)) breaths/min"
        case HKQuantityTypeIdentifier.environmentalAudioExposure.rawValue,
             HKQuantityTypeIdentifier.headphoneAudioExposure.rawValue:
            return "\(format(sample.quantity.doubleValue(for: .decibelAWeightedSoundPressureLevel()), maximumFractionDigits: 1)) dB"
        case HKQuantityTypeIdentifier.bodyMass.rawValue:
            return "\(format(sample.quantity.doubleValue(for: HKUnit.gramUnit(with: .kilo)), maximumFractionDigits: 1)) kg"
        case HKQuantityTypeIdentifier.vo2Max.rawValue:
            let unit = HKUnit.literUnit(with: .milli)
                .unitDivided(by: HKUnit.gramUnit(with: .kilo))
                .unitDivided(by: .minute())
            return "\(format(sample.quantity.doubleValue(for: unit), maximumFractionDigits: 1)) mL/kg/min"
        default:
            return "\(format(sample.quantity.doubleValue(for: .count()), maximumFractionDigits: 2))"
        }
    }

    private static func categoryDisplayValue(for sample: HKCategorySample) -> String {
        switch sample.categoryType.identifier {
        case HKCategoryTypeIdentifier.appleStandHour.rawValue:
            if sample.value == HKCategoryValueAppleStandHour.stood.rawValue {
                return "Stood"
            }
            if sample.value == HKCategoryValueAppleStandHour.idle.rawValue {
                return "Idle"
            }
            return "Stand hour \(sample.value)"
        case HKCategoryTypeIdentifier.sleepAnalysis.rawValue:
            return sleepValueLabel(sample.value)
        case HKCategoryTypeIdentifier.highHeartRateEvent.rawValue:
            return "High heart rate event"
        default:
            return "Category value \(sample.value)"
        }
    }

    private static func workoutDisplayValue(for workout: HKWorkout) -> String {
        let duration = format(workout.duration / 60, maximumFractionDigits: 0)
        return "\(workoutActivityName(workout.workoutActivityType)), \(duration) min"
    }

    private static func workoutActivityName(_ type: HKWorkoutActivityType) -> String {
        switch type {
        case .walking: return "Walking"
        case .running: return "Running"
        case .cycling: return "Cycling"
        case .swimming: return "Swimming"
        case .traditionalStrengthTraining: return "Strength training"
        case .functionalStrengthTraining: return "Functional strength training"
        case .highIntensityIntervalTraining: return "HIIT"
        case .yoga: return "Yoga"
        case .hiking: return "Hiking"
        case .mindAndBody: return "Mind and body"
        case .other: return "Workout"
        default: return "Workout \(type.rawValue)"
        }
    }

    private static func sleepValueLabel(_ value: Int) -> String {
        if value == HKCategoryValueSleepAnalysis.inBed.rawValue { return "In bed" }
        if value == HKCategoryValueSleepAnalysis.asleepUnspecified.rawValue { return "Asleep" }
        if value == HKCategoryValueSleepAnalysis.awake.rawValue { return "Awake" }
        if value == HKCategoryValueSleepAnalysis.asleepCore.rawValue { return "Core sleep" }
        if value == HKCategoryValueSleepAnalysis.asleepDeep.rawValue { return "Deep sleep" }
        if value == HKCategoryValueSleepAnalysis.asleepREM.rawValue { return "REM sleep" }
        return "Sleep value \(value)"
    }

    private static func format(_ value: Double, maximumFractionDigits: Int = 2, unit: String? = nil) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.maximumFractionDigits = maximumFractionDigits
        formatter.minimumFractionDigits = 0
        let formatted = formatter.string(from: NSNumber(value: value)) ?? "\(value)"
        if let unit, !unit.isEmpty {
            return "\(formatted) \(unit)"
        }
        return formatted
    }

    private func fetchEarliestDate(for sampleType: HKSampleType) async throws -> Date? {
        try await withCheckedThrowingContinuation { continuation in
            let query = HKSampleQuery(
                sampleType: sampleType,
                predicate: nil,
                limit: 1,
                sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)]
            ) { _, samples, error in
                if let error { continuation.resume(throwing: error); return }
                continuation.resume(returning: samples?.first?.startDate)
            }
            store.execute(query)
        }
    }

    private func fetchLatestDate(for sampleType: HKSampleType) async throws -> Date? {
        try await withCheckedThrowingContinuation { continuation in
            let query = HKSampleQuery(
                sampleType: sampleType,
                predicate: nil,
                limit: 1,
                sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)]
            ) { _, samples, error in
                if let error { continuation.resume(throwing: error); return }
                continuation.resume(returning: samples?.first?.startDate)
            }
            store.execute(query)
        }
    }

    // MARK: - Timeout profiles

    private func timeoutProfile(for monitoredType: MonitoredType) -> LocalMetricTimeoutProfile {
        LocalMetricTimeoutProfileStore.profile(for: monitoredType)
    }

    private func timeoutForFetch(
        profile: LocalMetricTimeoutProfile,
        adaptiveTimeoutsEnabled: Bool,
        timeoutMultiplier: Double
    ) -> TimeInterval {
        let base = adaptiveTimeoutsEnabled ? profile.effectiveTimeout : Self.defaultInitialTimeoutSeconds
        return min(max(base * timeoutMultiplier, Self.defaultInitialTimeoutSeconds), Self.maximumTimeoutSeconds)
    }

    private func updateTimeoutProfile(
        _ profile: inout LocalMetricTimeoutProfile,
        with result: TypeCountFetchResult,
        monitoredType: MonitoredType
    ) {
        if result.quality == .complete {
            profile.recordSuccess(elapsed: result.totalElapsedSeconds, displayName: monitoredType.displayName)
        } else if result.apiCalls.contains(where: { $0.status == .timeout }) {
            profile.recordTimeout(elapsed: result.totalElapsedSeconds, displayName: monitoredType.displayName)
        } else {
            profile.displayName = monitoredType.displayName
        }
    }

    // MARK: - Query diagnostics

    private struct APICallMeasurement<Value: Sendable>: Sendable {
        let value: Value?
        let apiCall: HealthKitAPICallResult
    }

    private func measureAPICall<Value: Sendable>(
        queryType: String,
        timeoutSeconds: TimeInterval,
        operation: @escaping @Sendable () async throws -> Value,
        resultDescription: @escaping @Sendable (Value) -> String
    ) async -> APICallMeasurement<Value> {
        let started = Date()
        do {
            guard timeoutSeconds > 0 else { throw CancellationError() }
            let value = try await withTimeout(seconds: timeoutSeconds, operation: operation)
            return APICallMeasurement(
                value: value,
                apiCall: HealthKitAPICallResult(
                    queryType: queryType,
                    status: .complete,
                    elapsedSeconds: Date().timeIntervalSince(started),
                    resultValue: resultDescription(value)
                )
            )
        } catch {
            let failure = Self.apiFailure(for: error)
            let nsError = error as NSError
            return APICallMeasurement(
                value: nil,
                apiCall: HealthKitAPICallResult(
                    queryType: queryType,
                    status: failure.status,
                    elapsedSeconds: Date().timeIntervalSince(started),
                    resultValue: nil,
                    errorCode: failure.errorCode ?? "\(nsError.code)",
                    errorDomain: failure.errorDomain ?? nsError.domain,
                    errorDescription: failure.errorDescription ?? nsError.localizedDescription,
                    failureKind: failure.failureKind,
                    cancellationReason: failure.cancellationReason
                )
            )
        }
    }

    private static func apiFailure(for error: Error) -> (
        status: HealthKitAPICallResult.Status,
        failureKind: String,
        errorDomain: String?,
        errorCode: String?,
        errorDescription: String?,
        cancellationReason: String?
    ) {
        if error is CancellationError {
            return (
                .timeout,
                "internal cancellation",
                "HealthProbe",
                "perTypeTimeout",
                "HealthProbe cancelled this query after the per-type timeout",
                "per-type timeout reached"
            )
        }

        let nsError = error as NSError
        if nsError.domain == NSURLErrorDomain && nsError.code == NSURLErrorTimedOut {
            return (.timeout, "timeout", nil, nil, nil, nil)
        }

        if (error as? HKError)?.code == .errorAuthorizationDenied {
            return (.unauthorized, "HealthKit error", nil, nil, nil, nil)
        }

        return (.failed, "HealthKit error", nil, nil, nil, nil)
    }

    private func authorizationStatus(from apiCalls: [HealthKitAPICallResult]) -> String {
        if apiCalls.contains(where: { $0.status == .complete }) {
            return "granted"
        }
        if apiCalls.contains(where: { $0.status == .unauthorized }) {
            return "denied"
        }
        return HealthKitAPICallResult.Status.unknown.rawValue
    }

    private func snapshotQuality(for status: HealthKitAPICallResult.Status) -> SnapshotQuality {
        status == .unauthorized ? .unauthorized : .failed
    }

    private func diagnosticQuality(for status: HealthKitAPICallResult.Status) -> String {
        switch status {
        case .complete:
            return HealthKitAPICallResult.Status.complete.rawValue
        case .unauthorized:
            return HealthKitAPICallResult.Status.unauthorized.rawValue
        case .unsupported:
            return HealthKitAPICallResult.Status.unsupported.rawValue
        case .timeout:
            return SnapshotQuality.failed.rawValue
        case .failed:
            return HealthKitAPICallResult.Status.failed.rawValue
        case .unknown:
            return HealthKitAPICallResult.Status.unknown.rawValue
        }
    }

    private func firstImpairedStatus(in apiCalls: [HealthKitAPICallResult]) -> HealthKitAPICallResult.Status {
        let statuses = apiCalls.map(\.status)
        if statuses.contains(.unauthorized) { return .unauthorized }
        if statuses.contains(.timeout) { return .timeout }
        if statuses.contains(.unsupported) { return .unsupported }
        if statuses.contains(.failed) { return .failed }
        return .unknown
    }

    private static func placeholderAPICalls(status: HealthKitAPICallResult.Status, message: String) -> [HealthKitAPICallResult] {
        [
            placeholderAPICall(queryType: "record_import", status: status, message: message),
            placeholderAPICall(queryType: "earliest_sample", status: status, message: message),
            placeholderAPICall(queryType: "latest_sample", status: status, message: message)
        ]
    }

    private static func placeholderAPICall(
        queryType: String,
        status: HealthKitAPICallResult.Status,
        message: String
    ) -> HealthKitAPICallResult {
        HealthKitAPICallResult(
            queryType: queryType,
            status: status,
            elapsedSeconds: 0,
            resultValue: nil,
            errorCode: "none",
            errorDomain: "none",
            errorDescription: message,
            failureKind: status == .unknown ? "not run" : status.rawValue,
            cancellationReason: "none"
        )
    }

    private static func iso8601String(for date: Date?) -> String {
        guard let date else { return "none" }
        return ISO8601DateFormatter().string(from: date)
    }

    // MARK: - Quality aggregation

    func deriveSnapshotQuality(from typeCounts: [TypeCount]) -> SnapshotQuality {
        guard !typeCounts.isEmpty else { return .failed }
        if typeCounts.contains(where: { $0.quality == .loading }) { return .loading }
        let allUnauthorized = typeCounts.allSatisfy { $0.quality == .unauthorized }
        if allUnauthorized { return .unauthorized }
        let anyImpaired = typeCounts.contains { $0.quality == .failed || $0.quality == .unauthorized }
        if anyImpaired { return .partial }
        return .complete
    }

    // MARK: - Chain helpers

    private func findPreviousSnapshot(deviceID: String, excluding id: UUID, context: ModelContext) -> HealthSnapshot? {
        var descriptor = FetchDescriptor<HealthSnapshot>(
            predicate: #Predicate<HealthSnapshot> { $0.deviceID == deviceID && $0.id != id },
            sortBy: [SortDescriptor(\.localSequenceNumber, order: .reverse)]
        )
        descriptor.fetchLimit = 1
        return try? context.fetch(descriptor).first
    }

    private func fetchSnapshot(id: UUID, context: ModelContext) -> HealthSnapshot? {
        var descriptor = FetchDescriptor<HealthSnapshot>(
            predicate: #Predicate<HealthSnapshot> { $0.id == id }
        )
        descriptor.fetchLimit = 1
        return try? context.fetch(descriptor).first
    }

    private func isStoreEmpty(context: ModelContext) -> Bool {
        var descriptor = FetchDescriptor<HealthSnapshot>()
        descriptor.fetchLimit = 1
        return (try? context.fetch(descriptor).isEmpty) ?? true
    }

    // MARK: - Device metadata

    private func hardwareModel() -> String {
        var size = 0
        sysctlbyname("hw.machine", nil, &size, nil, 0)
        var machine = [CChar](repeating: 0, count: size)
        sysctlbyname("hw.machine", &machine, &size, nil, 0)
        return String(cString: machine)
    }

    private func appBuildVersion() -> String {
        let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
        let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""
        return "\(version) (\(build))"
    }

    // MARK: - Timeout utility

    private func withTimeout<T: Sendable>(seconds: TimeInterval, operation: @escaping @Sendable () async throws -> T) async throws -> T {
        try await withThrowingTaskGroup(of: T.self) { group in
            group.addTask { try await operation() }
            group.addTask {
                try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
                throw CancellationError()
            }
            do {
                let result = try await group.next()!
                group.cancelAll()
                return result
            } catch {
                group.cancelAll()
                throw error
            }
        }
    }

    // MARK: - Type registry

    private static func buildAllTypes() -> [MonitoredType] {
        var result: [MonitoredType] = []

        func addQty(_ id: HKQuantityTypeIdentifier, _ name: String, _ cat: TypeCategory, on: Bool) {
            let t = HKObjectType.quantityType(forIdentifier: id)
            result.append(MonitoredType(id: t?.identifier ?? id.rawValue, displayName: name, category: cat, isEnabledByDefault: on, objectType: t))
        }

        func addCat(_ id: HKCategoryTypeIdentifier, _ name: String, _ cat: TypeCategory, on: Bool) {
            let t = HKObjectType.categoryType(forIdentifier: id)
            result.append(MonitoredType(id: t?.identifier ?? id.rawValue, displayName: name, category: cat, isEnabledByDefault: on, objectType: t))
        }

        let workout = HKObjectType.workoutType()
        result.append(MonitoredType(id: workout.identifier, displayName: "Workouts", category: .activity, isEnabledByDefault: true, objectType: workout))

        addQty(.stepCount,              "Steps",                          .activity,    on: true)
        addQty(.distanceWalkingRunning, "Walking + Running Distance",     .activity,    on: true)
        addQty(.activeEnergyBurned,     "Active Energy",                  .activity,    on: true)
        addQty(.appleExerciseTime,      "Exercise Minutes",               .activity,    on: true)
        addCat(.appleStandHour,         "Stand Hours",                    .activity,    on: true)

        addQty(.heartRate,              "Heart Rate",                     .heart,       on: true)
        addQty(.restingHeartRate,       "Resting Heart Rate",             .heart,       on: true)
        addCat(.highHeartRateEvent,     "High Heart Rate Notifications",  .heart,       on: true)

        addQty(.respiratoryRate,        "Respiratory Rate",               .respiratory, on: true)

        addCat(.sleepAnalysis,          "Sleep",                          .sleep,       on: true)

        addQty(.environmentalAudioExposure, "Environmental Sound Levels", .hearing,     on: false)
        addQty(.headphoneAudioExposure,     "Headphone Audio Levels",     .hearing,     on: false)

        addQty(.bodyMass, "Body Mass", .body, on: false)
        addQty(.vo2Max,   "VO2 Max",   .body, on: false)

        return result
    }
}

private struct SampleDistribution: Sendable {
    struct Bin: Sendable {
        let start: Date
        let end: Date
        let count: Int
        let contentHash: String
        let anchorData: Data?
    }
    let totalCount: Int
    let bins: [Bin]
    let records: [HealthRecordValue]
    let contentHash: String?
    let yearlyCounts: [Int: Int]?
    let recordArchiveData: Data?
}

private struct SampleDistributionPage: Sendable {
    let samples: [HKSample]
    let deletedObjects: [HKDeletedObject]
    let anchor: HKQueryAnchor?
}

private struct SampleRecordPayload: Sendable {
    let recordFingerprint: String
    let startDate: Date
    let endDate: Date
    let displayValue: String?
}

private enum HealthRecordArchiveReadError: LocalizedError {
    case missingArchive(typeIdentifier: String, count: Int)
    case decodeFailed(typeIdentifier: String)

    var errorDescription: String? {
        switch self {
        case .missingArchive(let typeIdentifier, let count):
            return "Missing record archive for \(typeIdentifier) with \(count) records."
        case .decodeFailed(let typeIdentifier):
            return "Could not decode previous record archive for \(typeIdentifier)."
        }
    }
}

private final class HealthKitQueryContinuationBox<Value: Sendable>: @unchecked Sendable {
    private let lock = NSLock()
    nonisolated(unsafe) private var continuation: CheckedContinuation<Value, Error>?
    nonisolated(unsafe) private var query: HKQuery?
    nonisolated(unsafe) private weak var store: HKHealthStore?
    nonisolated(unsafe) private var didResume = false

    nonisolated func setContinuation(_ continuation: CheckedContinuation<Value, Error>) {
        let shouldCancel: Bool
        lock.lock()
        if didResume {
            shouldCancel = true
        } else {
            shouldCancel = false
            self.continuation = continuation
        }
        lock.unlock()

        if shouldCancel {
            continuation.resume(throwing: CancellationError())
        }
    }

    nonisolated func setQuery(_ query: HKQuery, store: HKHealthStore) {
        lock.lock()
        self.query = query
        self.store = store
        let shouldStop = didResume
        lock.unlock()

        if shouldStop {
            store.stop(query)
        }
    }

    nonisolated func resume(returning value: Value) {
        complete { $0.resume(returning: value) }
    }

    nonisolated func resume(throwing error: Error) {
        complete { $0.resume(throwing: error) }
    }

    nonisolated func cancel() {
        let queryToStop: HKQuery?
        let storeToStop: HKHealthStore?
        lock.lock()
        queryToStop = query
        storeToStop = store
        lock.unlock()

        if let queryToStop, let storeToStop {
            storeToStop.stop(queryToStop)
        }

        resume(throwing: CancellationError())
    }

    nonisolated private func complete(_ resume: (CheckedContinuation<Value, Error>) -> Void) {
        let continuationToResume: CheckedContinuation<Value, Error>?
        lock.lock()
        if didResume {
            continuationToResume = nil
        } else {
            didResume = true
            continuationToResume = continuation
            continuation = nil
        }
        lock.unlock()

        if let continuationToResume {
            resume(continuationToResume)
        }
    }
}

private struct PreviousDistributionState: Sendable {
    struct Bin: Sendable {
        let bucketStart: Date
        let bucketEnd: Date
        let anchorData: Data?

        var anchor: HKQueryAnchor? {
            guard let anchorData else { return nil }
            return try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: anchorData)
        }
    }

    let typeIdentifier: String
    let count: Int
    let contentHash: String
    let earliestRecordDate: Date?
    let latestRecordDate: Date?
    let recordArchiveData: Data?
    let yearlyCounts: [Int: Int]
    let bins: [Bin]

    var globalAnchor: HKQueryAnchor? {
        guard bins.count == 1 else { return nil }
        return bins[0].anchor
    }

    init(typeCount: TypeCount?) {
        self.typeIdentifier = typeCount?.typeIdentifier ?? ""
        self.count = typeCount?.count ?? 0
        self.contentHash = typeCount?.contentHash ?? ""
        self.earliestRecordDate = typeCount?.earliestDate
        self.latestRecordDate = typeCount?.latestDate
        self.recordArchiveData = typeCount?.recordArchiveData
        self.yearlyCounts = Dictionary(
            uniqueKeysWithValues: (typeCount?.yearlyCounts ?? []).map { ($0.year, $0.count) }
        )
        self.bins = (typeCount?.distributionBins ?? []).map {
            Bin(
                bucketStart: $0.bucketStart,
                bucketEnd: $0.bucketEnd,
                anchorData: $0.anchorData
            )
        }
    }

    func unchangedDistribution(
        updatedAnchor: HKQueryAnchor?,
        typeIdentifier fallbackTypeIdentifier: String,
        earliestDate: Date?,
        latestDate: Date?
    ) -> SampleDistribution? {
        guard count >= 0,
              count == 0 || recordArchiveData != nil,
              count == 0 || !contentHash.isEmpty else {
            return nil
        }

        let binStart = earliestDate ?? earliestRecordDate ?? Date()
        let rawBinEnd = latestDate ?? latestRecordDate ?? binStart
        let binEnd = rawBinEnd > binStart ? rawBinEnd : binStart.addingTimeInterval(1)
        let effectiveTypeIdentifier = typeIdentifier.isEmpty ? fallbackTypeIdentifier : typeIdentifier
        let effectiveContentHash = contentHash.isEmpty
            ? HashService.typeHash(typeIdentifier: effectiveTypeIdentifier, recordFingerprints: [])
            : contentHash

        return SampleDistribution(
            totalCount: count,
            bins: [
                SampleDistribution.Bin(
                    start: binStart,
                    end: binEnd,
                    count: count,
                    contentHash: effectiveContentHash,
                    anchorData: updatedAnchor.flatMap(Self.archiveAnchor(_:))
                )
            ],
            records: [],
            contentHash: effectiveContentHash,
            yearlyCounts: yearlyCounts,
            recordArchiveData: recordArchiveData
        )
    }

    func makeRecordMap() throws -> [String: SampleRecordPayload] {
        guard let recordArchiveData else {
            if count > 0 {
                throw HealthRecordArchiveReadError.missingArchive(typeIdentifier: typeIdentifier, count: count)
            }
            return [:]
        }

        guard let records = HealthRecordArchive.decode(recordArchiveData) else {
            throw HealthRecordArchiveReadError.decodeFailed(typeIdentifier: typeIdentifier)
        }

        var recordMap: [String: SampleRecordPayload] = [:]
        recordMap.reserveCapacity(records.count)
        for record in records {
            recordMap[record.sampleUUIDHash] = SampleRecordPayload(
                recordFingerprint: record.recordFingerprint,
                startDate: record.startDate,
                endDate: record.endDate,
                displayValue: record.displayValue
            )
        }
        return recordMap
    }

    private nonisolated static func archiveAnchor(_ anchor: HKQueryAnchor) -> Data? {
        try? NSKeyedArchiver.archivedData(withRootObject: anchor, requiringSecureCoding: true)
    }
}

private struct TypeCountFetchResult: Sendable {
    struct YearlyCountData: Sendable {
        let year: Int
        let count: Int
        let typeIdentifier: String
        let isApproximate: Bool
    }
    struct RecordData: Sendable {
        let typeIdentifier: String
        let sampleUUIDHash: String
        let recordFingerprint: String
        let startDate: Date
        let endDate: Date
        let displayValue: String?
    }
    struct DistributionBinData: Sendable {
        let bucketStart: Date
        let bucketEnd: Date
        let count: Int
        let contentHash: String
        let anchorData: Data?
    }

    let typeIdentifier: String
    let displayName: String
    let count: Int
    let contentHash: String
    let earliestDate: Date?
    let latestDate: Date?
    let quality: SnapshotQuality
    let diagnosticQuality: String
    let isUnsupported: Bool
    let authorizationStatus: String
    let apiCalls: [HealthKitAPICallResult]
    let yearlyCounts: [YearlyCountData]
    let distributionBins: [DistributionBinData]
    let records: [RecordData]
    let recordArchiveData: Data?
    var timeoutConfiguredSeconds: TimeInterval = 0
    var totalElapsedSeconds: TimeInterval = 0
    var timeoutMode: String = "default"
    var lastSuccessfulElapsed: TimeInterval = 0
    var learnedTimeout: TimeInterval = 0
    var suggestedRetryTimeout: TimeInterval = 0
    var timeoutCount: Int = 0
    var successCount: Int = 0

    mutating func applyTimeoutProfile(_ profile: LocalMetricTimeoutProfile) {
        timeoutMode = profile.timeoutMode
        lastSuccessfulElapsed = profile.lastSuccessfulElapsed
        learnedTimeout = profile.effectiveTimeout
        suggestedRetryTimeout = profile.suggestedRetryTimeout
        timeoutCount = profile.timeoutCount
        successCount = profile.successCount
    }

    @MainActor
    func makeTypeCount() -> TypeCount {
        let typeCount = TypeCount(
            typeIdentifier: typeIdentifier,
            displayName: displayName,
            count: count,
            quality: quality
        )
        typeCount.contentHash = contentHash
        typeCount.earliestDate = earliestDate
        typeCount.latestDate = latestDate
        typeCount.isUnsupported = isUnsupported

        for yearlyCountData in yearlyCounts {
            let yearlyCount = YearlyCount(
                year: yearlyCountData.year,
                count: yearlyCountData.count,
                typeIdentifier: yearlyCountData.typeIdentifier,
                isApproximate: yearlyCountData.isApproximate
            )
            typeCount.yearlyCounts?.append(yearlyCount)
        }

        for binData in distributionBins {
            let bin = TypeDistributionBin(
                bucketStart: binData.bucketStart,
                bucketEnd: binData.bucketEnd,
                count: binData.count
            )
            bin.contentHash = binData.contentHash
            bin.anchorData = binData.anchorData
            typeCount.distributionBins?.append(bin)
        }

        if let recordArchiveData {
            typeCount.recordArchiveData = recordArchiveData
            typeCount.records?.removeAll()
        } else {
            typeCount.setRecordValues(records.map { recordData in
                HealthRecordValue(
                    typeIdentifier: recordData.typeIdentifier,
                    sampleUUIDHash: recordData.sampleUUIDHash,
                    recordFingerprint: recordData.recordFingerprint,
                    startDate: recordData.startDate,
                    endDate: recordData.endDate,
                    displayValue: recordData.displayValue
                )
            })
        }

        return typeCount
    }
}

private extension SnapshotFetchProgress {
    func updateDetails(from result: TypeCountFetchResult) {
        updateDetails(
            result.typeIdentifier,
            quality: result.diagnosticQuality,
            recordCount: result.count,
            isUnsupported: result.isUnsupported,
            authorizationStatus: result.authorizationStatus,
            earliestDate: result.earliestDate,
            latestDate: result.latestDate,
            yearlyCounts: result.yearlyCounts.map {
                SnapshotFetchProgress.YearlyCountProgress(
                    year: $0.year,
                    count: $0.count,
                    isApproximate: $0.isApproximate
                )
            },
            apiCallDetails: result.apiCalls,
            timeoutConfiguredSeconds: result.timeoutConfiguredSeconds,
            totalElapsedSeconds: result.totalElapsedSeconds,
            timeoutMode: result.timeoutMode,
            lastSuccessfulElapsed: result.lastSuccessfulElapsed,
            learnedTimeout: result.learnedTimeout,
            suggestedRetryTimeout: result.suggestedRetryTimeout,
            timeoutCount: result.timeoutCount,
            successCount: result.successCount
        )
    }

    func updateTimeoutProfile(from profile: LocalMetricTimeoutProfile, for typeID: String) {
        updateTimeoutProfile(
            typeID,
            timeoutMode: profile.timeoutMode,
            lastSuccessfulElapsed: profile.lastSuccessfulElapsed,
            learnedTimeout: profile.effectiveTimeout,
            suggestedRetryTimeout: profile.suggestedRetryTimeout,
            timeoutCount: profile.timeoutCount,
            successCount: profile.successCount
        )
    }
}

private enum DistributionCaptureConfiguration {
    static let queryPageLimit = 25_000
    static let pageTimeoutSeconds: TimeInterval = 60
    static let fullImportTimeoutSeconds: TimeInterval = HealthKitService.fullHistoryImportTimeoutSeconds
}
