import Foundation

enum ComparisonMode: Hashable {
    case previous
    case selected
    case relativeTime(TimeInterval)

    static let relativeOptions: [(label: String, interval: TimeInterval)] = [
        ("1 Hour Ago",  3_600),
        ("1 Day Ago",  86_400),
        ("1 Week Ago", 604_800),
    ]

    var label: String {
        switch self {
        case .previous:             return "Previous"
        case .selected:             return "Selected"
        case .relativeTime(let t):
            return Self.relativeOptions.first { $0.interval == t }?.label ?? "Custom"
        }
    }
}

@Observable
final class SnapshotsViewModel {
    var comparisonMode: ComparisonMode = .previous
    var selectedBaselineObservationID: Int64?
    var archiveRows: [CachedArchiveObservationRow]?
    var hasLoadedArchiveRows = false
    var archiveRowsError: String?

    @MainActor
    func loadArchiveRows(limit: Int = 200) async {
        do {
            let rows = try await SQLiteHealthArchiveStore.shared.observationRows(limit: limit)
            archiveRows = rows.isEmpty ? nil : rows
            hasLoadedArchiveRows = true
            archiveRowsError = nil
        } catch {
            archiveRows = nil
            hasLoadedArchiveRows = true
            archiveRowsError = error.localizedDescription
        }
    }

    func baselines(for rows: [CachedArchiveObservationRow]) -> [Int64: CachedArchiveObservationRow] {
        let orderedDescending = rows.sorted { $0.observedAt > $1.observedAt }

        return rows.reduce(into: [Int64: CachedArchiveObservationRow]()) { partial, row in
            partial[row.observationID] = baseline(
                for: row,
                in: rows,
                orderedDescending: orderedDescending
            )
        }
    }

    func baseline(
        for row: CachedArchiveObservationRow,
        in rows: [CachedArchiveObservationRow]
    ) -> CachedArchiveObservationRow? {
        baseline(
            for: row,
            in: rows,
            orderedDescending: rows.sorted { $0.observedAt > $1.observedAt }
        )
    }

    private func baseline(
        for row: CachedArchiveObservationRow,
        in rows: [CachedArchiveObservationRow],
        orderedDescending: [CachedArchiveObservationRow]
    ) -> CachedArchiveObservationRow? {
        switch comparisonMode {
        case .previous:
            return orderedDescending.first { $0.observedAt < row.observedAt }
        case .selected:
            guard let selectedBaselineObservationID else { return nil }
            return rows.first { $0.observationID == selectedBaselineObservationID }
        case .relativeTime(let interval):
            let target = row.observedAt.addingTimeInterval(-interval)
            return rows
                .filter { $0.observedAt <= target }
                .max { $0.observedAt < $1.observedAt }
        }
    }

    func toggleBaseline(_ row: CachedArchiveObservationRow) {
        if selectedBaselineObservationID == row.observationID {
            selectedBaselineObservationID = nil
        } else {
            selectedBaselineObservationID = row.observationID
            comparisonMode = .selected
        }
    }
}
