import Foundation
import UserNotifications

/// 周期性指标提醒的本地通知调度。
/// 同一 `metricId` 在 iOS 通知中心展开成 N 条 weekly-repeats 通知,id 形如
/// `kangkang.reminder.<metricId>.w<weekday>`,方便按 weekday 单独 cancel。
///
/// 数据存 SwiftData `MetricReminder`;本服务只负责系统通知中心的同步,
/// 不写 SwiftData。两边写入的协调由调用方负责。
enum ReminderService {

    static let idPrefix = "kangkang.reminder."
    static let customIdPrefix = "kangkang.custom."

    enum AuthState: String {
        case granted, denied, notDetermined, provisional
    }

    // MARK: - authorization

    static func currentAuthState() async -> AuthState {
        let settings = await UNUserNotificationCenter.current().notificationSettings()
        switch settings.authorizationStatus {
        case .authorized:        return .granted
        case .denied:            return .denied
        case .provisional:       return .provisional
        case .ephemeral:         return .granted
        case .notDetermined:     return .notDetermined
        @unknown default:        return .notDetermined
        }
    }

    /// 申请通知权限。已 granted/denied 时直接返回当前状态。
    @discardableResult
    static func requestAuthorization() async -> AuthState {
        let center = UNUserNotificationCenter.current()
        let settings = await center.notificationSettings()
        if settings.authorizationStatus != .notDetermined {
            return await currentAuthState()
        }
        do {
            let granted = try await center.requestAuthorization(options: [.alert, .sound, .badge])
            return granted ? .granted : .denied
        } catch {
            return .denied
        }
    }

    // MARK: - upsert / cancel

    /// 取消该 metric 在通知中心所有 pending 通知,再按当前 enabled/时间/weekdays 重排。
    /// 调用方在 `MetricReminder` save 之后调用。
    static func sync(_ reminder: MetricReminder) async {
        cancel(metricId: reminder.metricId)
        guard reminder.enabled else { return }
        let slots = reminder.weekdays.map { wd in
            Slot(suffix: "w\(wd)",
                 dc: DateComponents(hour: reminder.hour, minute: reminder.minute, weekday: wd))
        }
        await schedule(
            idBase: "\(idPrefix)\(reminder.metricId)",
            title: String(appLoc: "该测\(reminder.displayName)了"),
            body: String(appLoc: "在「+ 新建 → 指标记录 → \(reminder.displayName)」记录一次"),
            thread: "kangkang.reminder.\(reminder.metricId)",
            slots: slots
        )
    }

    /// 取消某个 metric 的所有 pending 通知(7 个 weekday 一并取消,不漏)。
    static func cancel(metricId: String) {
        cancelBase("\(idPrefix)\(metricId)")
    }

    // MARK: - 自由提醒(CustomReminder)

    /// 取消并按当前设置重排一条自由提醒。调用方在 `CustomReminder` save 之后调用。
    static func sync(_ reminder: CustomReminder) async {
        cancel(customId: reminder.id)
        guard reminder.enabled else { return }
        let title = reminder.title.trimmingCharacters(in: .whitespacesAndNewlines)
        let body = reminder.note.trimmingCharacters(in: .whitespacesAndNewlines)
        let h = reminder.hour, m = reminder.minute
        // 多选频率:把每个选中频率展开成槽,合并调度(suffix 各不冲突,可单独取消)。
        var slots: [Slot] = []
        for f in reminder.frequencies {
            switch f {
            case .daily:
                slots.append(Slot(suffix: "daily", dc: DateComponents(hour: h, minute: m)))
            case .weekly:
                slots += reminder.weekdays.map { wd in
                    Slot(suffix: "w\(wd)", dc: DateComponents(hour: h, minute: m, weekday: wd))
                }
            case .monthly:
                slots += reminder.monthlyDays.map { d in
                    Slot(suffix: "m\(d)", dc: DateComponents(day: d, hour: h, minute: m))
                }
            case .yearly:
                slots.append(Slot(suffix: "yearly",
                                  dc: DateComponents(month: reminder.month, day: reminder.dayOfMonth, hour: h, minute: m)))
            }
        }
        await schedule(
            idBase: "\(customIdPrefix)\(reminder.id.uuidString)",
            title: title.isEmpty ? String(appLoc: "提醒") : title,
            body: body.isEmpty ? String(appLoc: "到点啦,记得完成") : body,
            thread: "\(customIdPrefix)\(reminder.id.uuidString)",
            slots: slots
        )
    }

    /// 取消某条自由提醒的所有 pending 通知。
    static func cancel(customId: UUID) {
        cancelBase("\(customIdPrefix)\(customId.uuidString)")
    }

    /// 全清。Me Tab 一键关闭所有提醒时用。
    static func cancelAll() {
        UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
    }

    // MARK: - 共享调度核心

    /// 一条触发槽:`suffix` 用于拼出稳定且可单独取消的通知 id(`<idBase>.<suffix>`,
    /// 如 `.daily` / `.w2` / `.monthly` / `.yearly`),`dc` 为对应的重复触发时间分量。
    private struct Slot {
        let suffix: String
        let dc: DateComponents
    }

    /// 把若干 `Slot` 展开成 N 条 repeats 通知。每日/每周/每月/每年两类提醒共用本核心。
    private static func schedule(idBase: String,
                                 title: String,
                                 body: String,
                                 thread: String,
                                 slots: [Slot]) async {
        guard !slots.isEmpty else { return }
        let center = UNUserNotificationCenter.current()
        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body
        content.sound = .default
        content.threadIdentifier = thread

        for slot in slots {
            let trigger = UNCalendarNotificationTrigger(dateMatching: slot.dc, repeats: true)
            let request = UNNotificationRequest(identifier: "\(idBase).\(slot.suffix)",
                                                content: content,
                                                trigger: trigger)
            try? await center.add(request)
        }
    }

    /// 取消某个 idBase 下所有可能后缀的 pending 通知,不漏:
    /// daily / yearly / 旧版 monthly + 7 个 weekday(w1...w7)+ 31 个月内日(m1...m31)。
    private static func cancelBase(_ idBase: String) {
        let center = UNUserNotificationCenter.current()
        var ids = ["\(idBase).daily", "\(idBase).monthly", "\(idBase).yearly"]
        ids += (1...7).map { "\(idBase).w\($0)" }
        ids += (1...31).map { "\(idBase).m\($0)" }
        center.removePendingNotificationRequests(withIdentifiers: ids)
    }
}
