import Foundation
import SwiftData

enum IntegrityService {
    enum ValidationResult: Equatable {
        case valid
        case checksumMismatch(snapshotID: UUID, expected: String, actual: String)
        case missingDelta(fromID: UUID, toID: UUID)
        case corrupted(snapshotID: UUID, reason: String)
    }

    // Strict mode: used by chain traversal and analysis.
    // Recomputes checksum from TypeCounts; compares with stored delta.checksumAfter.
    // Returns .valid only if they match exactly.
    static func validate(snapshot: HealthSnapshot, delta: SnapshotDelta?) -> ValidationResult {
        guard let delta else {
            guard snapshot.isChainStart else {
                return .missingDelta(fromID: snapshot.previousSnapshotID ?? UUID(), toID: snapshot.id)
            }
            return .valid
        }

        let actual = HashService.snapshotChecksum(typeCounts: snapshot.typeCounts ?? [])
        guard actual == delta.checksumAfter else {
            return .checksumMismatch(snapshotID: snapshot.id, expected: delta.checksumAfter, actual: actual)
        }
        return .valid
    }

    // Strict chain walk via previousSnapshotID from latest backward.
    // Stops immediately at first missing delta or checksum mismatch — no skips, no auto-repair.
    // FORK DETECTION runs before traversal: if any previousSnapshotID value appears more than
    // once across the snapshot set, returns .corrupted immediately without traversal.
    static func validateChain(snapshots: [HealthSnapshot], deltas: [SnapshotDelta]) -> [ValidationResult] {
        // Fork detection: assert no duplicate previousSnapshotID values
        var seenPrevIDs: [UUID: UUID] = [:] // prevID → first snapshot ID that used it
        for snapshot in snapshots {
            guard let prevID = snapshot.previousSnapshotID else { continue }
            if let existingSnapshotID = seenPrevIDs[prevID] {
                return [.corrupted(
                    snapshotID: existingSnapshotID,
                    reason: "chain fork detected — two snapshots share the same previousSnapshotID"
                )]
            }
            seenPrevIDs[prevID] = snapshot.id
        }

        let snapshotByID = Dictionary(uniqueKeysWithValues: snapshots.map { ($0.id, $0) })
        let deltaByToID  = Dictionary(uniqueKeysWithValues: deltas.map { ($0.toSnapshotID, $0) })

        // Walk from the latest snapshot backward
        guard let latest = snapshots.max(by: { $0.localSequenceNumber < $1.localSequenceNumber }) else {
            return []
        }

        var results: [ValidationResult] = []
        var current: HealthSnapshot? = latest

        while let node = current {
            let delta = deltaByToID[node.id]
            let result = validate(snapshot: node, delta: delta)

            switch result {
            case .valid:
                break
            case .checksumMismatch, .missingDelta, .corrupted:
                // Strict mode — stop immediately on any error
                results.append(result)
                return results
            }

            if node.isChainStart {
                break
            }
            current = node.previousSnapshotID.flatMap { snapshotByID[$0] }
        }

        return results
    }
}
