import Foundation /// Einstellungen fuer Ablauf-Benachrichtigungen - je Server eigene. /// /// Rein auf dem Geraet gespeichert (UserDefaults), nicht am Server: Es ist eine /// Einstellung dieser App auf diesem iPhone, kein geteilter Serverzustand. /// Die Einheit, in der „X vor Ablauf" gemeint ist. enum LeadUnit: String, Codable, CaseIterable { case days, weeks var label: String { self == .weeks ? "Wochen" : "Tage" } } /// Eine Erinnerungsregel: „X Tage/Wochen vor Ablauf", optional nur fuer eine /// Kategorie, einmalig oder taeglich wiederkehrend. struct NotificationRule: Codable, Identifiable, Equatable { let id: UUID var amount: Int var unit: LeadUnit /// nil = alle Kategorien (die allgemeine Regel). var categoryId: Int? /// true = das Produkt taucht jeden Tag im Fenster (ab „X vorher" bis Ablauf) /// in der Tagesmeldung auf; false = nur am Tag „X vorher". var recurring: Bool init(id: UUID = UUID(), amount: Int = 3, unit: LeadUnit = .days, categoryId: Int? = nil, recurring: Bool = false) { self.id = id self.amount = amount self.unit = unit self.categoryId = categoryId self.recurring = recurring } /// Vorlaufzeit in Tagen - Wochen werden umgerechnet. var leadDays: Int { unit == .weeks ? amount * 7 : amount } } /// Alle Benachrichtigungseinstellungen eines Servers. struct ServerNotificationConfig: Codable, Equatable { var enabled: Bool = false /// Uhrzeit der Tagesmeldung. var hour: Int = 9 var minute: Int = 0 /// Letzte Warnung am Ablauftag („ACHTUNG: … abgelaufen"), fuer alle Produkte. var lastWarningOnExpiry: Bool = true var rules: [NotificationRule] = [] // MARK: Rueckgabe-Erinnerung fuer verliehene Einzelstuecke // // Bewusst schlichter als die MHD-Regeln: Ein Verleih hat genau ein Datum, // und nach Kategorien zu unterscheiden waere hier erfunden statt gebraucht. // Ein Verleih ohne Rueckgabedatum ist nie faellig und kommt nie vor. var loansEnabled: Bool = false /// „X vorher" – ein Hinweis, bevor die Rueckgabe ansteht. var loanLeadAmount: Int = 1 var loanLeadUnit: LeadUnit = .days /// Am Rueckgabetag selbst noch einmal. var loanOnDueDate: Bool = true /// Taeglich weiter, solange das Stueck ueberfaellig ist. var loanOverdue: Bool = true /// Vorlauf der Rueckgabe-Erinnerung in Tagen. var loanLeadDays: Int { loanLeadUnit == .weeks ? loanLeadAmount * 7 : loanLeadAmount } /// Alte gespeicherte Konfigurationen kennen die Verleih-Schluessel nicht – /// deshalb alle mit decodeIfPresent und Standardwert, sonst waere eine /// bestehende Einstellung nach dem Update nicht mehr lesbar. enum CodingKeys: String, CodingKey { case enabled, hour, minute, lastWarningOnExpiry, rules case loansEnabled, loanLeadAmount, loanLeadUnit, loanOnDueDate, loanOverdue } init() {} init(from decoder: Decoder) throws { let c = try decoder.container(keyedBy: CodingKeys.self) enabled = try c.decodeIfPresent(Bool.self, forKey: .enabled) ?? false hour = try c.decodeIfPresent(Int.self, forKey: .hour) ?? 9 minute = try c.decodeIfPresent(Int.self, forKey: .minute) ?? 0 lastWarningOnExpiry = try c.decodeIfPresent(Bool.self, forKey: .lastWarningOnExpiry) ?? true rules = try c.decodeIfPresent([NotificationRule].self, forKey: .rules) ?? [] loansEnabled = try c.decodeIfPresent(Bool.self, forKey: .loansEnabled) ?? false loanLeadAmount = try c.decodeIfPresent(Int.self, forKey: .loanLeadAmount) ?? 1 loanLeadUnit = try c.decodeIfPresent(LeadUnit.self, forKey: .loanLeadUnit) ?? .days loanOnDueDate = try c.decodeIfPresent(Bool.self, forKey: .loanOnDueDate) ?? true loanOverdue = try c.decodeIfPresent(Bool.self, forKey: .loanOverdue) ?? true } /// Ohne aktive Regel, ohne letzte Warnung und ohne Verleih-Erinnerung gibt /// es nichts zu planen – dann muss der Server auch nicht abgefragt werden. var hasSomethingToSchedule: Bool { enabled && (!rules.isEmpty || lastWarningOnExpiry || loansEnabled) } } /// Liest und schreibt die Konfigurationen. Ein Woerterbuch je Profil-UUID, /// analog zu `ProfileStore`. enum NotificationStore { static let key = "notification_configs" static func loadAll() -> [String: ServerNotificationConfig] { guard let data = UserDefaults.standard.data(forKey: key), let dict = try? JSONDecoder().decode([String: ServerNotificationConfig].self, from: data) else { return [:] } return dict } static func config(for profileID: UUID) -> ServerNotificationConfig { loadAll()[profileID.uuidString] ?? ServerNotificationConfig() } static func save(_ config: ServerNotificationConfig, for profileID: UUID) { var alle = loadAll() alle[profileID.uuidString] = config guard let data = try? JSONEncoder().encode(alle) else { return } UserDefaults.standard.set(data, forKey: key) } /// Beim Loeschen eines Profils die zugehoerige Konfiguration mit entfernen. static func remove(for profileID: UUID) { var alle = loadAll() alle.removeValue(forKey: profileID.uuidString) guard let data = try? JSONEncoder().encode(alle) else { return } UserDefaults.standard.set(data, forKey: key) } }