import EventKit
import Foundation

// MARK: - Models

public struct CalendarEntry: Sendable {
    public let calendarIdentifier: String
    public let title: String
    public let type: EKCalendarType
    public let color: String
    public let isSubscribed: Bool
    public let isImmutable: Bool
    public let isReadOnly: Bool
    public let source: String
}

public struct EventEntry: Sendable {
    public let eventIdentifier: String
    public let calendarIdentifier: String
    public let title: String
    public let notes: String?
    public let location: String?
    public let url: String?
    public let isAllDay: Bool
    public let startDate: Date
    public let endDate: Date
    public let creationDate: Date?
    public let lastModifiedDate: Date?
    public let status: EKEventStatus
    public let availability: EKEventAvailability
    public let isDetached: Bool
    public let hasAlarms: Bool
    public let hasRecurrenceRules: Bool
    public let recurrenceRule: String?
    public let organizerEmail: String?
    public let organizerName: String?
    public let attendeeCount: Int
}

// MARK: - Reader

/// Reads calendars and events from the system EventKit store.
public final class CalendarReader: @unchecked Sendable {
    public static let shared = CalendarReader()

    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.requestFullAccessToEvents()
                authorized = granted
            } catch {
                NSLog("CalendarReader: requestFullAccessToEvents failed: \(error)")
                authorized = false
            }
        } else {
            authorized = await withCheckedContinuation { continuation in
                store.requestAccess(to: .event) { granted, _ in
                    continuation.resume(returning: granted)
                }
            }
        }
        NSLog("CalendarReader: auth granted=\(authorized)")
        return authorized
    }

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

    // MARK: - Fetch

    public func fetchCalendars() -> [CalendarEntry] {
        store.calendars(for: .event).map { cal in
            CalendarEntry(
                calendarIdentifier: cal.calendarIdentifier,
                title: cal.title,
                type: cal.type,
                color: hexColor(from: cal.cgColor),
                isSubscribed: cal.isSubscribed,
                isImmutable: cal.isImmutable,
                isReadOnly: !cal.allowsContentModifications,
                source: cal.source?.title ?? ""
            )
        }
    }

    /// Fetch events modified on or after `since`, up to `lookaheadDays` into the future
    /// and `lookbackDays` into the past (both from now, not from `since`).
    public func fetchEvents(
        since: Date? = nil,
        lookbackDays: Int = 30,
        lookaheadDays: Int = 365
    ) -> [EventEntry] {
        let now = Date()
        let start = Calendar.current.date(byAdding: .day, value: -lookbackDays, to: now)!
        let end = Calendar.current.date(byAdding: .day, value: lookaheadDays, to: now)!

        let predicate = store.predicateForEvents(withStart: start, end: end, calendars: nil)
        let events = store.events(matching: predicate)
        NSLog("CalendarReader: fetchEvents raw=\(events.count) window=[\(lookbackDays)d ago … \(lookaheadDays)d ahead]")

        let filtered: [EKEvent]
        if let since {
            filtered = events.filter { event in
                (event.lastModifiedDate ?? event.creationDate ?? event.startDate) >= since
            }
        } else {
            filtered = events
        }

        NSLog("CalendarReader: fetchEvents filtered=\(filtered.count)")
        return filtered.map { mapEvent($0) }
    }

    // MARK: - Helpers

    private func mapEvent(_ event: EKEvent) -> EventEntry {
        let recurrenceRule: String? = event.recurrenceRules?.first.map { rule in
            // Encode as a human-readable string — server stores it as-is for display
            rule.frequency.description + (rule.recurrenceEnd != nil ? " (ends)" : " (repeats)")
        }

        return EventEntry(
            eventIdentifier: event.eventIdentifier ?? UUID().uuidString,
            calendarIdentifier: event.calendar?.calendarIdentifier ?? "",
            title: event.title ?? "(no title)",
            notes: event.notes,
            location: event.location,
            url: event.url?.absoluteString,
            isAllDay: event.isAllDay,
            startDate: event.startDate,
            endDate: event.endDate,
            creationDate: event.creationDate,
            lastModifiedDate: event.lastModifiedDate,
            status: event.status,
            availability: event.availability,
            isDetached: event.isDetached,
            hasAlarms: !(event.alarms?.isEmpty ?? true),
            hasRecurrenceRules: !(event.recurrenceRules?.isEmpty ?? true),
            recurrenceRule: recurrenceRule,
            organizerEmail: event.organizer?.url.absoluteString.replacingOccurrences(of: "mailto:", with: ""),
            organizerName: event.organizer?.name,
            attendeeCount: event.attendees?.count ?? 0
        )
    }

    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)
    }
}

// MARK: - EKRecurrenceFrequency description

private extension EKRecurrenceFrequency {
    var description: String {
        switch self {
        case .daily: return "daily"
        case .weekly: return "weekly"
        case .monthly: return "monthly"
        case .yearly: return "yearly"
        @unknown default: return "unknown"
        }
    }
}
