//
// ReminderPlanner.swift
// Workouts
//
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
//

import Foundation

/// A plain snapshot of the schedule fields the reminder planner needs (precedent:
/// `ScheduleFacts`). Built from `Schedule` cache entities by the scheduler.
struct ReminderSource: Sendable, Equatable {
    var id: String
    var routineName: String
    var recurrence: ScheduleRecurrence
    var weekdays: [Int]?      // Calendar weekday numbers 1 (Sun) – 7 (Sat); .fixedDays only
    var date: Date?           // the one-off day; .once only
    var reminderMinutes: Int? // minutes from midnight, local wall-clock; nil = no reminder
}

/// One notification request to schedule: a stable identifier (all carrying the
/// `reminder-` prefix so a resync can wipe exactly ours), the trigger's date
/// components, and whether it repeats.
struct ReminderPlan: Sendable, Equatable {
    var identifier: String
    var routineName: String
    var dateComponents: DateComponents
    var repeats: Bool
}

/// Pure derivation of notification plans from schedule snapshots (precedent:
/// `SeedReconcilePlanner` / `ProgressPlanner`) — the scheduler consumes its output,
/// so the trigger math is unit-testable without touching `UNUserNotificationCenter`.
enum ReminderPlanner {
    /// Every reminder identifier starts with this, so a full resync can remove all
    /// pending reminder requests (and nothing else) before re-adding.
    static let identifierPrefix = "reminder-"

    /// The plans for the given schedules. Schedules with no `reminderMinutes`
    /// contribute nothing; a `.once` schedule whose reminder moment is already past
    /// (or that has no date) contributes nothing.
    static func plans(
        schedules: [ReminderSource],
        calendar: Calendar = .current,
        now: Date = Date()
    ) -> [ReminderPlan] {
        schedules.flatMap { plans(for: $0, calendar: calendar, now: now) }
    }

    private static func plans(
        for schedule: ReminderSource,
        calendar: Calendar,
        now: Date
    ) -> [ReminderPlan] {
        guard let minutes = schedule.reminderMinutes else { return [] }
        let hour = minutes / 60
        let minute = minutes % 60

        switch schedule.recurrence {
        case .daily:
            var comps = DateComponents()
            comps.hour = hour
            comps.minute = minute
            return [ReminderPlan(identifier: identifierPrefix + schedule.id,
                                 routineName: schedule.routineName,
                                 dateComponents: comps, repeats: true)]

        case .fixedDays:
            return (schedule.weekdays ?? [])
                .filter { (1...7).contains($0) }
                .sorted()
                .map { weekday in
                    var comps = DateComponents()
                    comps.weekday = weekday
                    comps.hour = hour
                    comps.minute = minute
                    return ReminderPlan(identifier: "\(identifierPrefix)\(schedule.id)-\(weekday)",
                                        routineName: schedule.routineName,
                                        dateComponents: comps, repeats: true)
                }

        case .once:
            guard let day = schedule.date else { return [] }
            let startOfDay = calendar.startOfDay(for: day)
            guard let fireDate = calendar.date(byAdding: .minute, value: minutes, to: startOfDay),
                  fireDate > now else { return [] }
            let comps = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: fireDate)
            return [ReminderPlan(identifier: identifierPrefix + schedule.id,
                                 routineName: schedule.routineName,
                                 dateComponents: comps, repeats: false)]
        }
    }
}
