import Foundation
import Observation

enum RecordListMode {
    case disappeared(typeIdentifier: String)
    case added(typeIdentifier: String, afterDate: Date, beforeDate: Date)

    var typeIdentifier: String {
        switch self {
        case .disappeared(let typeID):
            return typeID
        case .added(let typeID, _, _):
            return typeID
        }
    }
}

enum ExportState: Equatable {
    case idle
    case loading
    case ready(URL)
    case failed(String)
}

@MainActor
@Observable
final class DataTypeRecordListViewModel {
    let typeIdentifier: String
    let displayName: String
    let totalCount: Int
    let mode: RecordListMode
    let previewRecords: [HealthRecordValue]

    var displayRecords: [HealthRecordValue] = []
    var isLoadingMore = false
    var hasMore: Bool { displayRecords.count < min(totalCount, previewRecords.count) }
    var exportState: ExportState = .idle

    private var pageIndex = 0
    private let pageSize = 50
    private let archiveStore: HealthArchiveStore

    init(
        typeIdentifier: String,
        displayName: String,
        totalCount: Int,
        mode: RecordListMode,
        previewRecords: [HealthRecordValue] = [],
        archiveStore: HealthArchiveStore
    ) {
        self.typeIdentifier = typeIdentifier
        self.displayName = displayName
        self.totalCount = totalCount
        self.mode = mode
        self.previewRecords = previewRecords
        self.archiveStore = archiveStore
    }

    func loadFirstPage() async {
        guard displayRecords.isEmpty else { return }
        pageIndex = 0
        await loadNextPage()
    }

    func loadNextPage() async {
        guard !isLoadingMore, hasMore else { return }
        isLoadingMore = true
        defer { isLoadingMore = false }

        let batchStart = pageIndex
        let batchEnd = min(pageIndex + pageSize, previewRecords.count)

        guard batchStart < previewRecords.count else {
            return
        }

        let batch = Array(previewRecords[batchStart..<batchEnd])

        // Enrich batch with actual values from archive store
        var enrichedBatch = batch
        do {
            let fingerprints = Set(batch.map { $0.recordFingerprint })
            let request = HealthArchiveRecordRequest(
                sampleTypeIdentifier: typeIdentifier,
                fingerprints: fingerprints,
                limit: nil
            )
            let archivedRecords = try await archiveStore.records(for: request)

            let archiveByFingerprint = Dictionary(grouping: archivedRecords, by: { $0.strictFingerprint })
            enrichedBatch = batch.map { cached in
                guard let archived = archiveByFingerprint[cached.recordFingerprint]?.first,
                      cached.displayValue == nil else { return cached }
                return HealthRecordValue(
                    typeIdentifier: cached.typeIdentifier,
                    sampleUUIDHash: cached.sampleUUIDHash,
                    recordFingerprint: cached.recordFingerprint,
                    startDate: cached.startDate,
                    endDate: cached.endDate,
                    displayValue: archived.displayValue
                )
            }
        } catch {
            // Cached preview records remain usable when the archive cannot enrich display values.
        }

        displayRecords.append(contentsOf: enrichedBatch)
        pageIndex = batchEnd
    }

    func exportAllRecords() async {
        exportState = .loading

        do {
            let reportRequest = buildExportRequest()
            let exportURL = try await archiveStore.exportReport(reportRequest)
            exportState = .ready(exportURL)
        } catch {
            exportState = .failed(error.localizedDescription)
        }
    }


    private func buildExportRequest() -> HealthArchiveReportRequest {
        switch mode {
        case .disappeared(let typeID):
            return HealthArchiveReportRequest(
                reportID: UUID(),
                title: "Disappeared Records - \(displayName)",
                typeIdentifierFilter: typeID,
                disappearedOnly: true
            )
        case .added(let typeID, let afterDate, let beforeDate):
            return HealthArchiveReportRequest(
                reportID: UUID(),
                title: "Added Records - \(displayName)",
                typeIdentifierFilter: typeID,
                firstSeenAfter: afterDate,
                firstSeenBefore: beforeDate
            )
        }
    }
}
