import Foundation

@Observable
final class DataTypesViewModel {
    var filter: DiffFilter = .all
    var comparisonMode: ComparisonMode = .previous
    var archiveDiffs: [TypeDiff]?
    var archiveDiffError: String?

    private let diffService = SnapshotDiffService.shared

    func diffs(current: HealthSnapshot?, snapshots: [HealthSnapshot]) -> [TypeDiff] {
        if let archiveDiffs {
            return diffService.apply(filter: filter, to: archiveDiffs)
        }
        guard let current else { return [] }
        guard let baseline = resolveBaseline(for: current, in: snapshots) else { return [] }
        let all = diffService.diff(current: current, baseline: baseline)
        return diffService.apply(filter: filter, to: all)
    }

    func baseline(for snapshot: HealthSnapshot, in snapshots: [HealthSnapshot]) -> HealthSnapshot? {
        resolveBaseline(for: snapshot, in: snapshots)
    }

    func clearArchiveDiffs() {
        archiveDiffs = nil
        archiveDiffError = nil
    }

    @MainActor
    func loadArchiveDiffs(current: HealthSnapshot?, snapshots: [HealthSnapshot]) async {
        guard let current,
              let baseline = resolveBaseline(for: current, in: snapshots),
              let currentObservationID = current.archiveObservationID,
              let baselineObservationID = baseline.archiveObservationID else {
            clearArchiveDiffs()
            return
        }

        do {
            let cache = try CoreDataArchiveCacheStore()
            let currentSummaries = try cache.typeSummaries(observationID: currentObservationID)
            let baselineSummaries = try cache.typeSummaries(observationID: baselineObservationID)
            let currentByType = Dictionary(uniqueKeysWithValues: currentSummaries.map { ($0.sampleTypeIdentifier, $0) })
            let baselineByType = Dictionary(uniqueKeysWithValues: baselineSummaries.map { ($0.sampleTypeIdentifier, $0) })
            let allTypeIdentifiers = Set(currentByType.keys).union(baselineByType.keys)

            var archiveRows: [TypeDiff] = []
            archiveRows.reserveCapacity(allTypeIdentifiers.count)

            for typeIdentifier in allTypeIdentifiers {
                let summary = currentByType[typeIdentifier]
                let baselineSummary = baselineByType[typeIdentifier]
                let diff = try await SQLiteHealthArchiveStore.shared.diffSummary(HealthArchiveDiffRequest(
                    fromObservationID: baselineObservationID,
                    toObservationID: currentObservationID,
                    sampleTypeIdentifier: typeIdentifier
                ))
                archiveRows.append(TypeDiff(
                    id: typeIdentifier,
                    typeIdentifier: typeIdentifier,
                    displayName: summary?.displayName ?? baselineSummary?.displayName ?? typeIdentifier,
                    currentCount: summary?.visibleRecordCount ?? 0,
                    previousCount: baselineSummary?.visibleRecordCount ?? 0,
                    previousTracked: baselineSummary != nil,
                    appearedCount: diff.appearedCount,
                    disappearedCount: diff.disappearedCount,
                    representationChangedCount: diff.representationChangedCount
                ))
            }

            archiveDiffs = archiveRows.sorted {
                $0.displayName.localizedCompare($1.displayName) == .orderedAscending
            }
            archiveDiffError = nil
        } catch {
            archiveDiffs = nil
            archiveDiffError = error.localizedDescription
        }
    }

    private func resolveBaseline(for snapshot: HealthSnapshot, in snapshots: [HealthSnapshot]) -> HealthSnapshot? {
        let sorted = snapshots.sorted { $0.timestamp > $1.timestamp }
        switch comparisonMode {
        case .previous:
            return sorted.first { $0.timestamp < snapshot.timestamp }
        case .selected:
            return nil  // DataTypesView uses .previous by default; selection lives in SnapshotsTab
        case .relativeTime(let interval):
            let target = snapshot.timestamp.addingTimeInterval(-interval)
            return diffService.nearest(to: target, in: snapshots)
        }
    }
}
