import AppKit
import Combine

/// Single source of truth for settings while the app is running.
/// Every change writes `config.json` straight away and tells AppDelegate to re-apply.
@MainActor
final class SettingsStore: ObservableObject {
    @Published var config: Config {
        didSet {
            config.save()
            onChange?(config)
        }
    }

    // Populated by AppDelegate so the UI can list the real calendars on this Mac.
    @Published var calendarAccess = false
    @Published var calendars: [CalendarWatcher.CalendarInfo] = []
    /// Set when macOS refuses to register the login item, so the UI can say so.
    @Published var loginItemError: String?

    /// Hooks back into the app.
    var onChange: ((Config) -> Void)?
    var onTestFire: (() -> Void)?
    var onRequestCalendarAccess: (() -> Void)?

    init(config: Config) {
        self.config = config
    }

    // MARK: - Convenience used by the calendar checklist

    func isCalendarOn(_ id: String) -> Bool {
        config.selectedCalendarIds.contains(id)
    }

    func setCalendar(_ id: String, on: Bool) {
        var ids = Set(config.selectedCalendarIds)
        if on { ids.insert(id) } else { ids.remove(id) }
        config.selectedCalendarIds = ids.sorted()
    }

    func setAllCalendars(on: Bool) {
        config.selectedCalendarIds = on ? calendars.map(\.id).sorted() : []
    }

    /// Calendars grouped by the account they came from — that's how you tell your own
    /// calendars apart from a client's, and how a ClickUp or Google feed shows up.
    var calendarsByAccount: [(account: String, items: [CalendarWatcher.CalendarInfo])] {
        Dictionary(grouping: calendars, by: \.accountName)
            .map { (account: $0.key, items: $0.value.sorted { $0.title < $1.title }) }
            .sorted { $0.account < $1.account }
    }
}
