import Foundation
import HealthKit

// Interface updated 2026-05-23 — see AGENTS.md
protocol HealthArchiveStore {
    func upsertSamples(_ samples: [HKSample], observedAt: Date) async throws -> HealthArchiveWriteSummary
    func markVerification(sampleType: HKSampleType, verifiedAt: Date) async throws
    func recordDisappearance(sampleUUIDHash: String, sampleTypeIdentifier: String, observedMissingAt: Date) async throws
    func records(for request: HealthArchiveRecordRequest) async throws -> [ArchivedHealthRecord]
    func diffSummary(_ request: HealthArchiveDiffRequest) async throws -> HealthArchiveDiffSummary
    func diffRecords(_ request: HealthArchiveDiffRecordRequest) async throws -> [ArchivedHealthRecord]
    func exportReport(_ request: HealthArchiveReportRequest) async throws -> URL
    func checkIntegrity() async throws -> HealthArchiveIntegrityReport
}

struct HealthArchiveWriteSummary: Equatable, Sendable {
    let insertedCount: Int
    let updatedCount: Int
    let unchangedCount: Int
}

struct HealthArchiveIntegrityReport: Equatable, Sendable {
    let schemaVersion: Int?
    let sqliteIntegrityStatus: String
    let foreignKeyIssueCount: Int
    let requiredTableNames: Set<String>
    let missingTableNames: Set<String>

    var passed: Bool {
        schemaVersion == 2 &&
        sqliteIntegrityStatus == "ok" &&
        foreignKeyIssueCount == 0 &&
        missingTableNames.isEmpty
    }
}

struct HealthArchiveRecordRequest: Equatable, Sendable {
    let visibleAtObservationID: Int64?
    let sampleTypeIdentifier: String?
    let fingerprints: Set<String>
    let disappearedOnly: Bool
    let firstSeenAfter: Date?
    let firstSeenBefore: Date?
    let afterCursor: RecordCursor?
    let limit: Int?

    nonisolated init(
        visibleAtObservationID: Int64? = nil,
        sampleTypeIdentifier: String? = nil,
        fingerprints: Set<String> = [],
        disappearedOnly: Bool = false,
        firstSeenAfter: Date? = nil,
        firstSeenBefore: Date? = nil,
        afterCursor: RecordCursor? = nil,
        limit: Int? = nil
    ) {
        self.visibleAtObservationID = visibleAtObservationID
        self.sampleTypeIdentifier = sampleTypeIdentifier
        self.fingerprints = fingerprints
        self.disappearedOnly = disappearedOnly
        self.firstSeenAfter = firstSeenAfter
        self.firstSeenBefore = firstSeenBefore
        self.afterCursor = afterCursor
        self.limit = limit
    }
}

struct RecordCursor: Equatable, Sendable {
    let startDate: Date
    let strictFingerprint: String
}

enum HealthArchiveDiffKind: String, Codable, Sendable {
    case appeared
    case disappeared
    case representationChanged
}

struct HealthArchiveDiffRequest: Equatable, Sendable {
    let fromObservationID: Int64
    let toObservationID: Int64
    let sampleTypeIdentifier: String?

    init(fromObservationID: Int64, toObservationID: Int64, sampleTypeIdentifier: String? = nil) {
        self.fromObservationID = fromObservationID
        self.toObservationID = toObservationID
        self.sampleTypeIdentifier = sampleTypeIdentifier
    }
}

struct HealthArchiveDiffSummary: Equatable, Sendable {
    let fromObservationID: Int64
    let toObservationID: Int64
    let sampleTypeIdentifier: String?
    let appearedCount: Int
    let disappearedCount: Int
    let representationChangedCount: Int

    var totalChangeCount: Int {
        appearedCount + disappearedCount + representationChangedCount
    }
}

struct HealthArchiveDiffRecordRequest: Equatable, Sendable {
    let fromObservationID: Int64
    let toObservationID: Int64
    let sampleTypeIdentifier: String?
    let kind: HealthArchiveDiffKind
    let afterCursor: RecordCursor?
    let limit: Int?

    init(
        fromObservationID: Int64,
        toObservationID: Int64,
        sampleTypeIdentifier: String? = nil,
        kind: HealthArchiveDiffKind,
        afterCursor: RecordCursor? = nil,
        limit: Int? = nil
    ) {
        self.fromObservationID = fromObservationID
        self.toObservationID = toObservationID
        self.sampleTypeIdentifier = sampleTypeIdentifier
        self.kind = kind
        self.afterCursor = afterCursor
        self.limit = limit
    }
}

struct HealthArchiveReportRequest: Equatable, Sendable {
    let reportID: UUID
    let title: String
    let includedFingerprints: Set<String>
    let typeIdentifierFilter: String?
    let disappearedOnly: Bool
    let firstSeenAfter: Date?
    let firstSeenBefore: Date?

    init(
        reportID: UUID,
        title: String,
        includedFingerprints: Set<String> = [],
        typeIdentifierFilter: String? = nil,
        disappearedOnly: Bool = false,
        firstSeenAfter: Date? = nil,
        firstSeenBefore: Date? = nil
    ) {
        self.reportID = reportID
        self.title = title
        self.includedFingerprints = includedFingerprints
        self.typeIdentifierFilter = typeIdentifierFilter
        self.disappearedOnly = disappearedOnly
        self.firstSeenAfter = firstSeenAfter
        self.firstSeenBefore = firstSeenBefore
    }
}

struct ArchivedHealthRecord: Identifiable, Equatable, Sendable, Encodable {
    let id: String
    let sampleTypeIdentifier: String
    let strictFingerprint: String
    let semanticFingerprint: String?
    let healthKitUUIDHash: String?
    let startDate: Date
    let endDate: Date
    let firstSeenAt: Date
    let lastSeenAt: Date?
    let lastVerifiedAt: Date?
    let disappearedAt: Date?

    // Value fields
    let valueKind: String?  // "quantity", "category", "workout", nil
    let value: Double?
    let unit: String?
    let categoryValue: Int?
    let workoutActivityType: Int?
    let durationSeconds: Double?

    // Source/device metadata
    let sourceName: String?
    let sourceBundleIdentifier: String?
    let deviceName: String?

    // Display helper
    var displayValue: String? {
        if let value, let unit {
            return "\(String(format: "%.1f", value)) \(unit)"
        }
        if let categoryValue {
            return "Category: \(categoryValue)"
        }
        if let durationSeconds {
            return String(format: "%.1f min", durationSeconds / 60.0)
        }
        return nil
    }
}
