/** * NotificationConfigDAO.swift * * Data Access Object (DAO) for NotificationConfig Core Data entity * Provides CRUD operations and query helpers for notification configuration * * @author Matthew Raymer * @version 1.0.0 * @created 2025-12-08 */ import Foundation import CoreData /** * Extension providing DAO methods for NotificationConfig entity * * This extension adds CRUD operations and query helpers to the * auto-generated NotificationConfig Core Data class. */ extension NotificationConfig { // MARK: - Constants private static let TAG = "DNP-NOTIFICATION-CONFIG-DAO" // MARK: - Create/Insert Methods /** * Create a new NotificationConfig entity in the given context * * @param context Core Data managed object context * @param id Unique configuration identifier * @param timesafariDid TimeSafari device ID * @param configType Type of configuration * @param configKey Configuration key * @param configValue Configuration value (string representation) * @param configDataType Data type of value (e.g., "string", "int", "bool", "json") * @param isEncrypted Whether value is encrypted * @param encryptionKeyId Encryption key identifier * @param ttlSeconds Time-to-live in seconds * @param isActive Whether configuration is active * @param metadata Additional metadata (JSON string) * @return Created NotificationConfig entity */ static func create( in context: NSManagedObjectContext, id: String, timesafariDid: String? = nil, configType: String? = nil, configKey: String? = nil, configValue: String? = nil, configDataType: String? = nil, isEncrypted: Bool = false, encryptionKeyId: String? = nil, ttlSeconds: Int64 = 604800, // 7 days default isActive: Bool = true, metadata: String? = nil ) -> NotificationConfig { let entity = NotificationConfig(context: context) let now = Date() entity.id = id entity.timesafariDid = timesafariDid entity.configType = configType entity.configKey = configKey entity.configValue = configValue entity.configDataType = configDataType entity.isEncrypted = isEncrypted entity.encryptionKeyId = encryptionKeyId entity.createdAt = now entity.updatedAt = now entity.ttlSeconds = ttlSeconds entity.isActive = isActive entity.metadata = metadata print("\(Self.TAG): Created NotificationConfig with id: \(id)") return entity } /** * Create from dictionary representation * * @param context Core Data managed object context * @param dict Dictionary with configuration data * @return Created NotificationConfig entity or nil */ static func create( in context: NSManagedObjectContext, from dict: [String: Any] ) -> NotificationConfig? { guard let id = dict["id"] as? String else { print("\(Self.TAG): Missing required 'id' field") return nil } // Convert createdAt/updatedAt if present let createdAt: Date if let createdMillis = dict["createdAt"] as? Int64 { createdAt = DailyNotificationDataConversions.dateFromEpochMillis(createdMillis) } else if let createdDate = dict["createdAt"] as? Date { createdAt = createdDate } else { createdAt = Date() } let updatedAt: Date if let updatedMillis = dict["updatedAt"] as? Int64 { updatedAt = DailyNotificationDataConversions.dateFromEpochMillis(updatedMillis) } else if let updatedDate = dict["updatedAt"] as? Date { updatedAt = updatedDate } else { updatedAt = Date() } let entity = NotificationConfig(context: context) entity.id = id entity.timesafariDid = dict["timesafariDid"] as? String entity.configType = dict["configType"] as? String entity.configKey = dict["configKey"] as? String entity.configValue = dict["configValue"] as? String entity.configDataType = dict["configDataType"] as? String entity.isEncrypted = dict["isEncrypted"] as? Bool ?? false entity.encryptionKeyId = dict["encryptionKeyId"] as? String entity.createdAt = createdAt entity.updatedAt = updatedAt entity.ttlSeconds = dict["ttlSeconds"] as? Int64 ?? 604800 entity.isActive = dict["isActive"] as? Bool ?? true entity.metadata = dict["metadata"] as? String return entity } // MARK: - Read/Query Methods /** * Fetch NotificationConfig by ID * * @param context Core Data managed object context * @param id Configuration ID * @return NotificationConfig entity or nil */ static func fetch( by id: String, in context: NSManagedObjectContext ) -> NotificationConfig? { let request: NSFetchRequest = NotificationConfig.fetchRequest() request.predicate = NSPredicate(format: "id == %@", id) request.fetchLimit = 1 do { let results = try context.fetch(request) return results.first } catch { print("\(Self.TAG): Error fetching by id: \(error.localizedDescription)") return nil } } /** * Fetch NotificationConfig by key (configKey) * * @param context Core Data managed object context * @param configKey Configuration key * @return NotificationConfig entity or nil */ static func fetchByConfigKey( _ configKey: String, in context: NSManagedObjectContext ) -> NotificationConfig? { let request: NSFetchRequest = NotificationConfig.fetchRequest() request.predicate = NSPredicate(format: "configKey == %@", configKey) request.fetchLimit = 1 do { let results = try context.fetch(request) return results.first } catch { print("\(Self.TAG): Error fetching by configKey: \(error.localizedDescription)") return nil } } /** * Fetch all NotificationConfig entities * * @param context Core Data managed object context * @return Array of NotificationConfig entities */ static func fetchAll( in context: NSManagedObjectContext ) -> [NotificationConfig] { let request: NSFetchRequest = NotificationConfig.fetchRequest() do { return try context.fetch(request) } catch { print("\(Self.TAG): Error fetching all: \(error.localizedDescription)") return [] } } /** * Query by timesafariDid * * @param context Core Data managed object context * @param timesafariDid TimeSafari device ID * @return Array of NotificationConfig entities */ static func queryByTimesafariDid( _ timesafariDid: String, in context: NSManagedObjectContext ) -> [NotificationConfig] { let request: NSFetchRequest = NotificationConfig.fetchRequest() request.predicate = NSPredicate(format: "timesafariDid == %@", timesafariDid) request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)] do { return try context.fetch(request) } catch { print("\(Self.TAG): Error querying by timesafariDid: \(error.localizedDescription)") return [] } } /** * Query by configType * * @param context Core Data managed object context * @param configType Configuration type * @return Array of NotificationConfig entities */ static func queryByConfigType( _ configType: String, in context: NSManagedObjectContext ) -> [NotificationConfig] { let request: NSFetchRequest = NotificationConfig.fetchRequest() request.predicate = NSPredicate(format: "configType == %@", configType) request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)] do { return try context.fetch(request) } catch { print("\(Self.TAG): Error querying by configType: \(error.localizedDescription)") return [] } } /** * Query active configurations only * * @param context Core Data managed object context * @return Array of active NotificationConfig entities */ static func queryActive( in context: NSManagedObjectContext ) -> [NotificationConfig] { let request: NSFetchRequest = NotificationConfig.fetchRequest() request.predicate = NSPredicate(format: "isActive == YES") request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)] do { return try context.fetch(request) } catch { print("\(Self.TAG): Error querying active: \(error.localizedDescription)") return [] } } /** * Query by configType and isActive * * @param context Core Data managed object context * @param configType Configuration type * @param isActive Whether configuration is active * @return Array of NotificationConfig entities */ static func query( by configType: String, isActive: Bool, in context: NSManagedObjectContext ) -> [NotificationConfig] { let request: NSFetchRequest = NotificationConfig.fetchRequest() request.predicate = NSPredicate( format: "configType == %@ AND isActive == %@", configType, NSNumber(value: isActive) ) request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)] do { return try context.fetch(request) } catch { print("\(Self.TAG): Error querying by configType and isActive: \(error.localizedDescription)") return [] } } // MARK: - Update Methods /** * Update configuration value * * @param value New configuration value */ func updateValue(_ value: String?) { self.configValue = value self.updatedAt = Date() } /** * Activate or deactivate configuration * * @param active Whether configuration should be active */ func setActive(_ active: Bool) { self.isActive = active self.updatedAt = Date() } /** * Update this entity's updatedAt timestamp */ func touch() { self.updatedAt = Date() } // MARK: - Delete Methods /** * Delete NotificationConfig by ID * * @param context Core Data managed object context * @param id Configuration ID * @return true if deleted, false otherwise */ static func delete( by id: String, in context: NSManagedObjectContext ) -> Bool { guard let entity = fetch(by: id, in: context) else { return false } context.delete(entity) do { try context.save() print("\(Self.TAG): Deleted NotificationConfig with id: \(id)") return true } catch { print("\(Self.TAG): Error deleting: \(error.localizedDescription)") context.rollback() return false } } /** * Delete NotificationConfig by configKey * * @param context Core Data managed object context * @param configKey Configuration key * @return true if deleted, false otherwise */ static func deleteByConfigKey( _ configKey: String, in context: NSManagedObjectContext ) -> Bool { guard let entity = fetchByConfigKey(configKey, in: context) else { return false } context.delete(entity) do { try context.save() print("\(Self.TAG): Deleted NotificationConfig with configKey: \(configKey)") return true } catch { print("\(Self.TAG): Error deleting: \(error.localizedDescription)") context.rollback() return false } } /** * Delete all NotificationConfig entities * * @param context Core Data managed object context * @return Number of entities deleted */ static func deleteAll( in context: NSManagedObjectContext ) -> Int { let request: NSFetchRequest = NotificationConfig.fetchRequest() let deleteRequest = NSBatchDeleteRequest(fetchRequest: request) do { let result = try context.execute(deleteRequest) as? NSBatchDeleteResult try context.save() let count = result?.result as? Int ?? 0 print("\(Self.TAG): Deleted \(count) NotificationConfig entities") return count } catch { print("\(Self.TAG): Error deleting all: \(error.localizedDescription)") context.rollback() return 0 } } }