import Foundation import SwiftData /// Snapshot eines `DueReview` (Review + Card-Subset) zum Zeitpunkt des /// letzten Sync. Wird verwendet, wenn `StudySession` keine Verbindung /// zum Server bekommt — der User lernt dann die Karten, die zum Sync- /// Zeitpunkt fällig waren. /// /// Server-authoritative-FSRS bleibt: die `due`/`stability`/… Werte /// kommen vom Server, lokal wird nie gerechnet. Beim nächsten Sync /// liefert der Server eine neue Due-Liste. @Model final class CachedDueReview { /// Eindeutiger Schlüssel: `"-"`. SwiftData braucht /// einen primären Identifier pro Model, das natürliche compound- /// Schlüssel auf cardId+subIndex. @Attribute(.unique) var compoundId: String var cardId: String var subIndex: Int var deckId: String var userId: String // Review-State (server-authoritative, hier nur Snapshot) var due: Date var stability: Double var difficulty: Double var elapsedDays: Double var scheduledDays: Double var learningSteps: Int var reps: Int var lapses: Int var stateRaw: String var lastReview: Date? // Card-Snapshot (für offline-Rendering — was die Study-View braucht) var cardType: String var cardFields: [String: String] var snapshottedAt: Date init(dueReview: DueReview, deckId: String, userId: String) { compoundId = "\(dueReview.review.cardId)-\(dueReview.review.subIndex)" cardId = dueReview.review.cardId subIndex = dueReview.review.subIndex self.deckId = deckId self.userId = userId due = dueReview.review.due stability = dueReview.review.stability difficulty = dueReview.review.difficulty elapsedDays = dueReview.review.elapsedDays scheduledDays = dueReview.review.scheduledDays learningSteps = dueReview.review.learningSteps reps = dueReview.review.reps lapses = dueReview.review.lapses stateRaw = dueReview.review.state.rawValue lastReview = dueReview.review.lastReview cardType = dueReview.card.type.rawValue cardFields = dueReview.card.fields snapshottedAt = .now } /// Rekonstruiert einen `DueReview` für die `StudySession`-Queue. /// Gibt `nil` zurück, wenn der Type/State im Enum nicht mehr existiert /// (z.B. nach Schema-Migration). func toDueReview() -> DueReview? { guard let state = ReviewState(rawValue: stateRaw), let type = CardType(rawValue: cardType) else { return nil } let review = Review( cardId: cardId, subIndex: subIndex, userId: userId, due: due, stability: stability, difficulty: difficulty, elapsedDays: elapsedDays, scheduledDays: scheduledDays, learningSteps: learningSteps, reps: reps, lapses: lapses, state: state, lastReview: lastReview ) let card = ReviewCard(id: cardId, deckId: deckId, type: type, fields: cardFields) return DueReview(review: review, card: card) } }