// ── Contextual Pre-Warning Messages ─────────────────────────── // Keyword → helpful prep message mapping. Expandable, no LLM needed. // Used in notification body and on timeline to give actionable context. // Ported from web/src/lib/context-messages.ts import Foundation // MARK: - Context Rule struct ContextRule { let keywords: [String] let messages: [String] } // MARK: - Rules Engine private let contextRules: [ContextRule] = [ // Meetings & calls ContextRule( keywords: ["meeting", "standup", "sync", "huddle", "scrum", "1:1", "one-on-one"], messages: ["Review your agenda", "Check meeting notes", "Prepare talking points"] ), ContextRule( keywords: ["call", "phone", "dial"], messages: ["Have the number ready", "Review call notes", "Find a quiet spot"] ), ContextRule( keywords: ["interview", "screening"], messages: ["Review the job description", "Prepare your questions", "Test your camera and mic"] ), ContextRule( keywords: ["presentation", "demo", "pitch"], messages: ["Run through your slides", "Check your screen sharing", "Have backup ready"] ), // Travel & transport ContextRule( keywords: ["flight", "plane", "airport"], messages: ["Check in online", "Verify gate number", "Pack your carry-on"] ), ContextRule( keywords: ["train", "bus", "subway", "metro"], messages: ["Check for delays", "Have your ticket ready"] ), ContextRule( keywords: ["drive", "commute", "carpool", "uber", "lyft", "taxi", "cab"], messages: ["Check traffic conditions", "Grab your keys"] ), ContextRule( keywords: ["pickup", "pick up", "drop off", "dropoff"], messages: ["Confirm the location", "Check for any updates"] ), // Health & wellness ContextRule( keywords: ["doctor", "dentist", "therapist", "appointment", "clinic", "hospital"], messages: ["Bring your insurance card", "Leave with travel buffer", "Note any symptoms to mention"] ), ContextRule( keywords: ["medicine", "medication", "pill", "vitamin"], messages: ["Take with water", "Check dosage"] ), ContextRule( keywords: ["workout", "exercise", "gym", "run", "yoga", "stretch"], messages: ["Hydrate beforehand", "Change into workout clothes", "Warm up first"] ), // Food & cooking ContextRule( keywords: ["cook", "cooking", "bake", "baking", "oven", "recipe"], messages: ["Preheat the oven", "Gather your ingredients", "Check you have everything"] ), ContextRule( keywords: ["pasta", "noodle", "rice", "boil"], messages: ["Start boiling water", "Salt the water"] ), ContextRule( keywords: ["dinner", "lunch", "breakfast", "meal", "eat"], messages: ["Start prepping ingredients", "Set the table"] ), ContextRule( keywords: ["laundry", "washer", "dryer", "clothes"], messages: ["Move clothes to dryer", "Check pockets first"] ), // Work & productivity ContextRule( keywords: ["deadline", "due", "submit", "submission"], messages: ["Final review before submitting", "Double-check requirements"] ), ContextRule( keywords: ["class", "lecture", "lesson", "school", "study"], messages: ["Pack your materials", "Review last session notes"] ), ContextRule( keywords: ["focus", "deep work", "concentrate"], messages: ["Close unnecessary tabs", "Put phone on silent", "Grab water"] ), // Personal ContextRule( keywords: ["birthday", "anniversary", "celebration", "party"], messages: ["Check if the gift is ready", "Confirm the plan"] ), ContextRule( keywords: ["walk", "dog", "pet"], messages: ["Grab the leash", "Bring waste bags"] ), ContextRule( keywords: ["sleep", "bed", "bedtime", "wind down", "rest"], messages: ["Start winding down", "Put away screens", "Set tomorrow's alarm"] ), ContextRule( keywords: ["wake", "morning", "alarm"], messages: ["Time to get up!", "Stretch and hydrate"] ), ] // MARK: - Lookup /// Get a contextual message for a timer label. /// Returns the first matching message, or nil if no match. func getContextMessage(label: String) -> String? { let lower = label.lowercased() for rule in contextRules { if rule.keywords.contains(where: { lower.contains($0) }) { return rule.messages.first } } return nil } /// Get all matching contextual messages for a timer label. /// Returns an array of messages from all matching rules. func getAllContextMessages(label: String) -> [String] { let lower = label.lowercased() var messages: [String] = [] for rule in contextRules { if rule.keywords.contains(where: { lower.contains($0) }) { if let first = rule.messages.first { messages.append(first) } } } return messages } /// Get a contextual message formatted for a pre-warning notification. /// Includes the time remaining context. func getWarningMessage(label: String, minutesBefore: Int) -> String { let contextMsg = getContextMessage(label: label) let timeStr: String if minutesBefore >= 60 { let hours = minutesBefore / 60 let mins = minutesBefore % 60 timeStr = mins > 0 ? "\(hours)h \(mins)m" : "\(hours)h" } else { timeStr = "\(minutesBefore)m" } let base = "\(label) in \(timeStr)" if let msg = contextMsg { return "\(base) — \(msg)" } return base } /// Check if a label matches any context rule. func hasContextMatch(label: String) -> Bool { return getContextMessage(label: label) != nil } /// Get all registered context rules (for UI display / editing). func getContextRules() -> [ContextRule] { return contextRules }