import Foundation
import SwiftData

struct HealthRecordValue: Codable, Hashable, Identifiable, Sendable {
    var id: String { recordFingerprint }
    let typeIdentifier: String
    let sampleUUIDHash: String
    let recordFingerprint: String
    let startDate: Date
    let endDate: Date
    let displayValue: String?
}

enum HealthRecordArchive {
    private static let compactMagic = Data([0x48, 0x50, 0x52, 0x41, 0x32]) // HPRA2

    static func isCompact(_ data: Data) -> Bool {
        data.starts(with: compactMagic)
    }

    static func compactedIfNeeded(_ data: Data) -> Data? {
        if isCompact(data) {
            return data
        }

        guard let decoded = decode(data) else {
            return nil
        }
        return encode(decoded)
    }

    static func encode(_ values: [HealthRecordValue]) -> Data? {
        guard let typeIdentifier = values.first?.typeIdentifier else {
            return encodeCompact(typeIdentifier: "", values: [])
        }
        return encodeCompact(typeIdentifier: typeIdentifier, values: values)
    }

    static func decode(_ data: Data) -> [HealthRecordValue]? {
        if let decoded = decodeCompact(data) {
            return decoded
        }
        return try? PropertyListDecoder().decode([HealthRecordValue].self, from: data)
    }

    static func forEachRecord(in data: Data, _ body: (HealthRecordValue) -> Void) -> Bool {
        if isCompact(data) {
            return forEachCompactRecord(in: data, body)
        }

        guard let decoded = try? PropertyListDecoder().decode([HealthRecordValue].self, from: data) else {
            return false
        }
        for value in decoded {
            body(value)
        }
        return true
    }

    static func fingerprintSet(from data: Data?) -> Set<String>? {
        guard let data else { return [] }

        MemoryLog.log("healthRecordArchive.fingerprintSet.begin", metadata: [
            "archive": MemoryLog.format(UInt64(data.count)),
            "format": isCompact(data) ? "compact" : "plist"
        ])
        if isCompact(data) {
            let fingerprints = compactFingerprintSet(from: data)
            MemoryLog.log("healthRecordArchive.fingerprintSet.end", metadata: [
                "archive": MemoryLog.format(UInt64(data.count)),
                "fingerprints": "\(fingerprints?.count ?? 0)",
                "success": "\(fingerprints != nil)"
            ])
            return fingerprints
        }

        var fingerprints = Set<String>()
        let didRead = forEachRecord(in: data) { value in
            fingerprints.insert(value.recordFingerprint)
        }
        MemoryLog.log("healthRecordArchive.fingerprintSet.end", metadata: [
            "archive": MemoryLog.format(UInt64(data.count)),
            "fingerprints": "\(fingerprints.count)",
            "success": "\(didRead)"
        ])
        return didRead ? fingerprints : nil
    }

    private static func compactFingerprintSet(from data: Data) -> Set<String>? {
        guard data.starts(with: compactMagic) else { return nil }
        var reader = CompactReader(data: data, offset: compactMagic.count)
        guard reader.skipString(),
              let count = reader.readUInt64() else {
            return nil
        }

        let expectedCount = Int(min(count, UInt64(Int.max)))
        var fingerprints = Set<String>()
        fingerprints.reserveCapacity(expectedCount)

        for _ in 0..<count {
            guard reader.skipString(),
                  let recordFingerprint = reader.readString(),
                  reader.skipDouble(),
                  reader.skipDouble(),
                  reader.skipOptionalString() else {
                return nil
            }
            fingerprints.insert(recordFingerprint)
        }

        return fingerprints
    }

    static func makeCompactWriter(typeIdentifier: String, estimatedRecordCount: Int = 0) -> CompactWriter {
        CompactWriter(typeIdentifier: typeIdentifier, estimatedRecordCount: estimatedRecordCount)
    }

    private static func encodeCompact(typeIdentifier: String, values: [HealthRecordValue]) -> Data? {
        var writer = makeCompactWriter(typeIdentifier: typeIdentifier, estimatedRecordCount: values.count)
        for value in values {
            writer.append(value)
        }
        return writer.finalize()
    }

    struct CompactWriter {
        private var data = Data()
        private var countOffset = 0
        private var count: UInt64 = 0

        init(typeIdentifier: String, estimatedRecordCount: Int = 0) {
            data.reserveCapacity(max(256, estimatedRecordCount * 160))
            data.append(compactMagic)
            appendString(typeIdentifier)
            countOffset = data.count
            appendUInt64(0)
        }

        mutating func append(_ value: HealthRecordValue) {
            appendString(value.sampleUUIDHash)
            appendString(value.recordFingerprint)
            appendDouble(value.startDate.timeIntervalSinceReferenceDate)
            appendDouble(value.endDate.timeIntervalSinceReferenceDate)
            appendOptionalString(value.displayValue)
            count += 1
        }

        mutating func finalize() -> Data {
            var littleEndianCount = count.littleEndian
            data.replaceSubrange(
                countOffset..<countOffset + MemoryLayout<UInt64>.size,
                with: withUnsafeBytes(of: &littleEndianCount) { Array($0) }
            )
            return data
        }

        private mutating func appendString(_ value: String) {
            let bytes = Array(value.utf8)
            appendUInt32(UInt32(bytes.count))
            data.append(contentsOf: bytes)
        }

        private mutating func appendOptionalString(_ value: String?) {
            guard let value else {
                appendInt32(-1)
                return
            }
            let bytes = Array(value.utf8)
            appendInt32(Int32(bytes.count))
            data.append(contentsOf: bytes)
        }

        private mutating func appendUInt32(_ value: UInt32) {
            var littleEndian = value.littleEndian
            data.append(contentsOf: withUnsafeBytes(of: &littleEndian) { Array($0) })
        }

        private mutating func appendInt32(_ value: Int32) {
            var littleEndian = value.littleEndian
            data.append(contentsOf: withUnsafeBytes(of: &littleEndian) { Array($0) })
        }

        private mutating func appendUInt64(_ value: UInt64) {
            var littleEndian = value.littleEndian
            data.append(contentsOf: withUnsafeBytes(of: &littleEndian) { Array($0) })
        }

        private mutating func appendDouble(_ value: Double) {
            appendUInt64(value.bitPattern)
        }
    }

    private static func decodeCompact(_ data: Data) -> [HealthRecordValue]? {
        var values: [HealthRecordValue] = []
        var expectedCount = 0
        guard forEachCompactRecord(in: data, onHeader: { count in
            expectedCount = count
            values.reserveCapacity(count)
        }, body: { value in
            values.append(value)
        }) else {
            return nil
        }
        guard values.count == expectedCount else { return nil }
        return values
    }

    private static func forEachCompactRecord(
        in data: Data,
        _ body: (HealthRecordValue) -> Void
    ) -> Bool {
        forEachCompactRecord(in: data, onHeader: { _ in }, body: body)
    }

    private static func forEachCompactRecord(
        in data: Data,
        onHeader: (Int) -> Void,
        body: (HealthRecordValue) -> Void
    ) -> Bool {
        guard data.starts(with: compactMagic) else { return false }
        var reader = CompactReader(data: data, offset: compactMagic.count)
        guard let typeIdentifier = reader.readString(),
              let count = reader.readUInt64() else {
            return false
        }

        onHeader(Int(min(count, UInt64(Int.max))))
        for _ in 0..<count {
            guard let sampleUUIDHash = reader.readString(),
                  let recordFingerprint = reader.readString(),
                  let startInterval = reader.readDouble(),
                  let endInterval = reader.readDouble(),
                  let displayValue = reader.readOptionalString() else {
                return false
            }
            body(
                HealthRecordValue(
                    typeIdentifier: typeIdentifier,
                    sampleUUIDHash: sampleUUIDHash,
                    recordFingerprint: recordFingerprint,
                    startDate: Date(timeIntervalSinceReferenceDate: startInterval),
                    endDate: Date(timeIntervalSinceReferenceDate: endInterval),
                    displayValue: displayValue
                )
            )
        }
        return true
    }

    private struct CompactReader {
        let data: Data
        var offset: Int

        mutating func readString() -> String? {
            guard let length = readUInt32(),
                  let value = readUTF8String(count: Int(length)) else {
                return nil
            }
            return value
        }

        mutating func readOptionalString() -> String?? {
            guard let length = readInt32() else { return nil }
            if length < 0 {
                return .some(nil)
            }
            guard let value = readUTF8String(count: Int(length)) else { return nil }
            return .some(value)
        }

        mutating func readUInt32() -> UInt32? {
            readFixedWidthInteger()
        }

        mutating func readInt32() -> Int32? {
            readFixedWidthInteger()
        }

        mutating func readUInt64() -> UInt64? {
            readFixedWidthInteger()
        }

        mutating func readDouble() -> Double? {
            guard let bitPattern: UInt64 = readFixedWidthInteger() else { return nil }
            return Double(bitPattern: bitPattern)
        }

        private mutating func readUTF8String(count: Int) -> String? {
            guard count >= 0, offset + count <= data.count else { return nil }
            let startOffset = offset
            offset += count
            return data.withUnsafeBytes { rawBuffer in
                guard let baseAddress = rawBuffer.baseAddress else {
                    return count == 0 ? "" : nil
                }
                let buffer = UnsafeBufferPointer(
                    start: baseAddress.advanced(by: startOffset).assumingMemoryBound(to: UInt8.self),
                    count: count
                )
                return String(decoding: buffer, as: UTF8.self)
            }
        }

        private mutating func readFixedWidthInteger<T: FixedWidthInteger>() -> T? {
            let size = MemoryLayout<T>.size
            guard size >= 0, offset + size <= data.count else { return nil }
            let startOffset = offset
            offset += size
            var rawValue: UInt64 = 0
            for byteOffset in 0..<size {
                rawValue |= UInt64(data[startOffset + byteOffset]) << UInt64(byteOffset * 8)
            }
            return T(truncatingIfNeeded: rawValue)
        }

        mutating func skipString() -> Bool {
            guard let length = readUInt32() else { return false }
            return skipBytes(count: Int(length))
        }

        mutating func skipOptionalString() -> Bool {
            guard let length = readInt32() else { return false }
            if length < 0 { return true }
            return skipBytes(count: Int(length))
        }

        mutating func skipDouble() -> Bool {
            skipBytes(count: MemoryLayout<UInt64>.size)
        }

        private mutating func skipBytes(count: Int) -> Bool {
            guard count >= 0, offset + count <= data.count else { return false }
            offset += count
            return true
        }
    }
}
// Interface updated 2026-05-12 — see AGENTS.md
@Model final class HealthRecord {
    var id: UUID = UUID()
    var typeIdentifier: String = ""
    var sampleUUIDHash: String = ""
    var recordFingerprint: String = ""
    var startDate: Date = Date.distantPast
    var endDate: Date = Date.distantPast
    var displayValue: String?
    var typeCount: TypeCount?

    init(
        typeIdentifier: String,
        sampleUUIDHash: String,
        recordFingerprint: String,
        startDate: Date,
        endDate: Date,
        displayValue: String? = nil
    ) {
        self.id = UUID()
        self.typeIdentifier = typeIdentifier
        self.sampleUUIDHash = sampleUUIDHash
        self.recordFingerprint = recordFingerprint
        self.startDate = startDate
        self.endDate = endDate
        self.displayValue = displayValue
    }
}
