import Foundation
import UserNotifications
import Observation

/// Reminder time slots based on ritual TimeOfDay values.
/// Groups similar times to avoid excessive notifications.
enum ReminderSlot: String, CaseIterable {
    case morning    // 7:00 AM - covers morning rituals
    case midday     // 12:00 PM - covers midday and afternoon rituals  
    case evening    // 6:00 PM - covers evening and night rituals
    
    var hour: Int {
        switch self {
        case .morning: return 7
        case .midday: return 12
        case .evening: return 18
        }
    }
    
    var notificationId: String {
        "rituals.reminder.\(rawValue)"
    }
    
    var title: String {
        switch self {
        case .morning: return String(localized: "Good morning")
        case .midday: return String(localized: "Midday check-in")
        case .evening: return String(localized: "Evening rituals")
        }
    }
    
    /// Maps TimeOfDay values to their corresponding reminder slot
    static func slot(for timeOfDay: TimeOfDay) -> ReminderSlot? {
        switch timeOfDay {
        case .morning:
            return .morning
        case .midday, .afternoon:
            return .midday
        case .evening, .night:
            return .evening
        case .anytime:
            return nil  // Anytime rituals don't trigger specific reminders
        }
    }
}

/// Schedules smart reminders based on active rituals' time-of-day settings.
/// Only schedules reminders for time slots that have active rituals.
@MainActor
@Observable
final class ReminderScheduler {
    
    private(set) var authorizationStatus: UNAuthorizationStatus = .notDetermined
    private(set) var scheduledSlots: Set<ReminderSlot> = []
    
    /// Whether reminders are enabled by the user
    var remindersEnabled: Bool {
        get { UserDefaults.standard.bool(forKey: "remindersEnabled") }
        set {
            UserDefaults.standard.set(newValue, forKey: "remindersEnabled")
            if newValue {
                Task { await requestAuthorizationAndSchedule() }
            } else {
                cancelAllReminders()
            }
        }
    }
    
    /// The rituals to base reminders on - set this from RitualStore
    private var activeRituals: [Ritual] = []
    
    init() {
        Task { await refreshAuthorizationStatus() }
    }
    
    // MARK: - Public API
    
    /// Updates the scheduled reminders based on the current active rituals.
    /// Call this whenever rituals are created, updated, or deleted.
    func updateReminders(for rituals: [Ritual]) async {
        // Filter to rituals that have an active arc (currently in progress)
        activeRituals = rituals.filter { $0.hasActiveArc }
        
        guard remindersEnabled else {
            cancelAllReminders()
            return
        }
        
        await scheduleRemindersForActiveSlots()
    }
    
    /// Requests notification authorization from the user.
    func requestAuthorization() async -> Bool {
        do {
            let granted = try await UNUserNotificationCenter.current().requestAuthorization(
                options: [.alert, .sound, .badge]
            )
            await refreshAuthorizationStatus()
            return granted
        } catch {
            print("Notification authorization error: \(error)")
            return false
        }
    }
    
    /// Cancels all scheduled ritual reminders.
    func cancelAllReminders() {
        let ids = ReminderSlot.allCases.map { $0.notificationId }
        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ids)
        scheduledSlots = []
        clearBadge()
    }
    
    /// Clears the app badge count.
    func clearBadge() {
        UNUserNotificationCenter.current().setBadgeCount(0) { _ in }
    }

    /// Refreshes authorization status and reschedules if enabled.
    func refreshStatus() async {
        await refreshAuthorizationStatus()
        if remindersEnabled {
            await scheduleRemindersForActiveSlots()
        }
    }
    
    // MARK: - Private
    
    private func refreshAuthorizationStatus() async {
        let settings = await UNUserNotificationCenter.current().notificationSettings()
        authorizationStatus = settings.authorizationStatus
    }
    
    private func requestAuthorizationAndSchedule() async {
        let authorized = await requestAuthorization()
        if authorized {
            await scheduleRemindersForActiveSlots()
        }
    }
    
    private func scheduleRemindersForActiveSlots() async {
        // Determine which slots have active rituals
        var neededSlots = Set<ReminderSlot>()
        
        for ritual in activeRituals {
            if let slot = ReminderSlot.slot(for: ritual.timeOfDay) {
                neededSlots.insert(slot)
            }
        }
        
        // Cancel slots that are no longer needed
        let slotsToCancel = scheduledSlots.subtracting(neededSlots)
        for slot in slotsToCancel {
            UNUserNotificationCenter.current().removePendingNotificationRequests(
                withIdentifiers: [slot.notificationId]
            )
        }
        
        // Schedule slots that are needed but not yet scheduled
        let slotsToSchedule = neededSlots.subtracting(scheduledSlots)
        for slot in slotsToSchedule {
            await scheduleReminder(for: slot)
        }
        
        scheduledSlots = neededSlots
    }
    
    private func scheduleReminder(for slot: ReminderSlot) async {
        // Count rituals for this slot
        let ritualsForSlot = activeRituals.filter { ritual in
            ReminderSlot.slot(for: ritual.timeOfDay) == slot
        }
        
        let content = UNMutableNotificationContent()
        content.title = slot.title
        
        if ritualsForSlot.count == 1, let ritual = ritualsForSlot.first {
            content.body = String(localized: "Time for \(ritual.title)")
        } else {
            content.body = String(localized: "You have \(ritualsForSlot.count) rituals to complete")
        }
        
        content.sound = .default
        content.badge = 1
        
        // Create daily trigger at the slot's hour
        var triggerComponents = DateComponents()
        triggerComponents.hour = slot.hour
        triggerComponents.minute = 0
        
        let trigger = UNCalendarNotificationTrigger(
            dateMatching: triggerComponents,
            repeats: true
        )
        
        let request = UNNotificationRequest(
            identifier: slot.notificationId,
            content: content,
            trigger: trigger
        )
        
        do {
            try await UNUserNotificationCenter.current().add(request)
        } catch {
            print("Failed to schedule \(slot.rawValue) reminder: \(error)")
        }
    }
}
