import Foundation
import SwiftData

struct TypeCountDetailCacheResolution: Sendable {
    let cache: TypeCountDetailCache?
    let diagnostic: String
}

extension TypeCount {
    private static let detailCacheResolverVersion = "resolver-v5"

    @MainActor
    func ensureRecordArchiveDataIfNeeded() -> Bool {
        if count <= 0 {
            return true
        }

        if let existingArchive = recordArchiveData {
            if HealthRecordArchive.isCompact(existingArchive) {
                return true
            }

            guard let compactArchive = HealthRecordArchive.compactedIfNeeded(existingArchive) else {
                return false
            }
            recordArchiveData = compactArchive
            return true
        }

        let legacyRecords = records ?? []
        guard !legacyRecords.isEmpty else {
            return false
        }

        let values = legacyRecords.map { record in
            HealthRecordValue(
                typeIdentifier: record.typeIdentifier,
                sampleUUIDHash: record.sampleUUIDHash,
                recordFingerprint: record.recordFingerprint,
                startDate: record.startDate,
                endDate: record.endDate,
                displayValue: record.displayValue
            )
        }
        guard let archive = HealthRecordArchive.encode(values) else {
            return false
        }

        recordArchiveData = archive
        records?.removeAll()
        return true
    }

    @MainActor
    func resolveDetailCache(
        previous: TypeCount?,
        baselineSnapshotID: UUID?,
        context: ModelContext,
        source: String
    ) -> TypeCountDetailCache? {
        resolveDetailCacheWithDiagnostics(
            previous: previous,
            baselineSnapshotID: baselineSnapshotID,
            context: context,
            source: source
        ).cache
    }

    @MainActor
    func resolveDetailCacheWithDiagnostics(
        previous: TypeCount?,
        baselineSnapshotID: UUID?,
        context: ModelContext,
        source: String
    ) -> TypeCountDetailCacheResolution {
        if let cache = detailCache,
           cache.matchesBaseline(baselineSnapshotID) {
            return TypeCountDetailCacheResolution(
                cache: cache,
                diagnostic: detailCacheDiagnostic(
                    previous: previous,
                    baselineSnapshotID: baselineSnapshotID,
                    phase: "cache-hit"
                )
            )
        }

        let currentArchiveWasMissing = count > 0 && recordArchiveData == nil
        let previousArchiveWasMissing = (previous?.count ?? 0) > 0 && previous?.recordArchiveData == nil
        let currentArchiveAvailable = ensureRecordArchiveDataIfNeeded()
        let previousArchiveAvailable = previous?.ensureRecordArchiveDataIfNeeded() ?? true

        guard currentArchiveAvailable, previousArchiveAvailable else {
            let diagnostic = detailCacheDiagnostic(
                previous: previous,
                baselineSnapshotID: baselineSnapshotID,
                phase: "missing-archive"
            )
            MemoryLog.log("\(source).detailCache.resolveUnavailable", metadata: detailCacheMetadata(previous: previous).merging([
                "diagnostic": diagnostic
            ]) { _, new in new })
            return TypeCountDetailCacheResolution(cache: nil, diagnostic: diagnostic)
        }

        MemoryLog.log("\(source).detailCache.buildBegin", metadata: detailCacheMetadata(previous: previous))
        let cache = TypeCountDetailCacheBuilder.build(
            current: self,
            previous: previous,
            baselineSnapshotID: baselineSnapshotID
        )
        let diagnostic = detailCacheDiagnostic(
            previous: previous,
            baselineSnapshotID: baselineSnapshotID,
            phase: cache == nil ? "build-nil" : "built"
        )
        MemoryLog.log("\(source).detailCache.buildEnd", metadata: [
            "source": source,
            "type": typeIdentifier,
            "cacheBuilt": "\(cache != nil)",
            "diagnostic": diagnostic
        ])

        if let cache {
            setDetailCache(cache)
        }

        if cache != nil ||
            (currentArchiveWasMissing && recordArchiveData != nil) ||
            (previousArchiveWasMissing && previous?.recordArchiveData != nil) {
            try? context.save()
        }

        return TypeCountDetailCacheResolution(cache: cache, diagnostic: diagnostic)
    }

    private func detailCacheMetadata(previous: TypeCount?) -> [String: String] {
        [
            "type": typeIdentifier,
            "currentCount": "\(count)",
            "previousCount": "\(previous?.count ?? 0)",
            "currentArchive": recordArchiveData.map { MemoryLog.format(UInt64($0.count)) } ?? "nil",
            "previousArchive": previous?.recordArchiveData.map { MemoryLog.format(UInt64($0.count)) } ?? "nil"
        ]
    }

    private func detailCacheDiagnostic(
        previous: TypeCount?,
        baselineSnapshotID: UUID?,
        phase: String
    ) -> String {
        let baseline = baselineSnapshotID?.uuidString.prefix(6) ?? "none"
        let currentArchive = archiveDebugLabel(for: self)
        let previousArchive = archiveDebugLabel(for: previous)
        return "\(Self.detailCacheResolverVersion) phase=\(phase) base=\(baseline) curr=\(currentArchive) prev=\(previousArchive)"
    }

    private func archiveDebugLabel(for typeCount: TypeCount?) -> String {
        guard let typeCount else { return "none" }
        if let archive = typeCount.recordArchiveData {
            let formatSuffix = HealthRecordArchive.isCompact(archive) ? "-c" : "-p"
            return "\(MemoryLog.format(UInt64(archive.count)))\(formatSuffix)"
        }
        if typeCount.count <= 0 {
            return "empty"
        }
        return "missing"
    }
}
