import EventKit
import Foundation
import LilithLogging

private let log = AppLogger.logger(for: "IReminder.Reader")

// MARK: - Models

public struct ReminderCalendarEntry: Sendable, Equatable {
    public let calendarIdentifier: String
    public let title: String
    public let source: String
    public let color: String

    public init(
        calendarIdentifier: String,
        title: String,
        source: String,
        color: String
    ) {
        self.calendarIdentifier = calendarIdentifier
        self.title = title
        self.source = source
        self.color = color
    }
}

public struct ReminderEntry: Sendable, Equatable {
    public let reminderIdentifier: String
    public let calendarIdentifier: String
    public let title: String?
    public let notes: String?
    public let dueDate: Date?
    public let priority: Int
    public let isCompleted: Bool
    public let creationDate: Date?
    public let lastModifiedDate: Date?

    public init(
        reminderIdentifier: String,
        calendarIdentifier: String,
        title: String?,
        notes: String?,
        dueDate: Date?,
        priority: Int,
        isCompleted: Bool,
        creationDate: Date?,
        lastModifiedDate: Date?
    ) {
        self.reminderIdentifier = reminderIdentifier
        self.calendarIdentifier = calendarIdentifier
        self.title = title
        self.notes = notes
        self.dueDate = dueDate
        self.priority = priority
        self.isCompleted = isCompleted
        self.creationDate = creationDate
        self.lastModifiedDate = lastModifiedDate
    }
}

// MARK: - Reader

/// Reads reminder lists and reminders from the system EventKit store
/// (`EKEntityType.reminder`).
public final class ReminderReader: @unchecked Sendable {
    public static let shared = ReminderReader()

    private let store = EKEventStore()
    private var authorized = false

    private init() {}

    /// Exposed for the Sender, which needs to call `save`/`remove` on the same
    /// authorized store the Reader uses.
    public var eventStore: EKEventStore { store }

    // MARK: - Authorization

    public func requestAuthorization() async -> Bool {
        if #available(macOS 14.0, *) {
            do {
                let granted = try await store.requestFullAccessToReminders()
                authorized = granted
            } catch {
                NSLog("ReminderReader: requestFullAccessToReminders failed: \(error)")
                authorized = false
            }
        } else {
            authorized = await withCheckedContinuation { continuation in
                store.requestAccess(to: .reminder) { granted, _ in
                    continuation.resume(returning: granted)
                }
            }
        }
        NSLog("ReminderReader: auth granted=\(authorized)")
        return authorized
    }

    public var isAuthorized: Bool {
        if #available(macOS 14.0, *) {
            EKEventStore.authorizationStatus(for: .reminder) == .fullAccess
        } else {
            EKEventStore.authorizationStatus(for: .reminder) == .authorized
        }
    }

    // MARK: - Fetch

    public func fetchCalendars() -> [ReminderCalendarEntry] {
        store.calendars(for: .reminder).map { cal in
            ReminderCalendarEntry(
                calendarIdentifier: cal.calendarIdentifier,
                title: cal.title,
                source: cal.source?.title ?? "",
                color: hexColor(from: cal.cgColor)
            )
        }
    }

    /// Fetch all reminders (across all reminder lists). If `since` is supplied,
    /// reminders whose `lastModifiedDate` is strictly older than `since` are
    /// dropped (matches the calendar reader's delta semantics).
    public func fetchReminders(since: Date? = nil) async -> [ReminderEntry] {
        let predicate = store.predicateForReminders(in: nil)
        let raw: [EKReminder] = await withCheckedContinuation { continuation in
            store.fetchReminders(matching: predicate) { reminders in
                continuation.resume(returning: reminders ?? [])
            }
        }
        NSLog("ReminderReader: fetchReminders raw=\(raw.count)")

        let filtered: [EKReminder]
        if let since {
            filtered = raw.filter { r in
                (r.lastModifiedDate ?? r.creationDate ?? Date.distantPast) >= since
            }
        } else {
            filtered = raw
        }

        NSLog("ReminderReader: fetchReminders filtered=\(filtered.count)")
        return filtered.map { mapReminder($0) }
    }

    // MARK: - Helpers

    private func mapReminder(_ r: EKReminder) -> ReminderEntry {
        ReminderEntry(
            reminderIdentifier: r.calendarItemIdentifier,
            calendarIdentifier: r.calendar?.calendarIdentifier ?? "",
            title: r.title,
            notes: r.notes,
            dueDate: r.dueDateComponents?.date,
            priority: r.priority,
            isCompleted: r.isCompleted,
            creationDate: r.creationDate,
            lastModifiedDate: r.lastModifiedDate
        )
    }

    private func hexColor(from cgColor: CGColor?) -> String {
        guard let cgColor, let components = cgColor.components, components.count >= 3 else {
            return "#888888"
        }
        let r = Int(components[0] * 255)
        let g = Int(components[1] * 255)
        let b = Int(components[2] * 255)
        return String(format: "#%02X%02X%02X", r, g, b)
    }
}
