import AppKit
import Combine
import EventKit
import SwiftUI

struct CalendarEventSnapshot: Identifiable, Hashable {
    let id: String
    let title: String
    let start: Date
    let end: Date
    let isAllDay: Bool
    let location: String?
}

struct CalendarAgendaDay: Identifiable, Hashable {
    let id: String
    let dayLabel: String
    let events: [CalendarEventSnapshot]
}

/// Google Calendar desktop widget: local EventKit data + deep links into calendar.google.com.
struct CalendarDesktopWidgetView: View {
    let app: LauncherApp
    let mode: CalendarWidgetMode
    var isPreview: Bool = false

    @StateObject private var model = CalendarWidgetEventsModel()

    private var baseURL: URL { app.webURL ?? URL(string: "https://calendar.google.com")! }

    var body: some View {
        Group {
            switch mode {
            case .compact:
                compactBody
            case .agenda:
                agendaBody
            }
        }
        .clipShape(RoundedRectangle(cornerRadius: 22, style: .continuous))
        .task(id: isPreview) {
            await model.refresh(isPreview: isPreview)
        }
    }

    // MARK: - Small

    private var compactBody: some View {
        ZStack(alignment: .topLeading) {
            calendarGradientBackground
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            VStack(alignment: .leading, spacing: 12) {
                HStack(spacing: 10) {
                    AppIconView(app: app, size: 32, showAppBackground: false, iconPaddingFactor: 0.08)
                    VStack(alignment: .leading, spacing: 2) {
                        Text(app.name)
                            .font(.system(size: 13, weight: .bold))
                            .foregroundStyle(.white.opacity(0.95))
                        Text("Today")
                            .font(.system(size: 10, weight: .semibold))
                            .foregroundStyle(.white.opacity(0.65))
                    }
                    Spacer(minLength: 0)
                }

                todayDateBlock

                if model.accessDenied && !isPreview {
                    Text("Calendar access is off. Enable it in System Settings to see events.")
                        .font(.system(size: 11, weight: .medium))
                        .foregroundStyle(.white.opacity(0.7))
                        .fixedSize(horizontal: false, vertical: true)
                    Button(action: openPrivacyCalendars) {
                        Text("Open Privacy settings")
                            .font(.system(size: 11, weight: .bold))
                    }
                    .buttonStyle(.plain)
                    .foregroundStyle(Color(red: 0.75, green: 0.9, blue: 1))
                } else {
                    Text("Next up")
                        .font(.system(size: 10, weight: .bold))
                        .foregroundStyle(.white.opacity(0.5))

                    if let ev = model.nextEvent {
                        nextEventSummary(ev)
                    } else {
                        Text(isPreview ? "Sample event at 2:00 PM" : "No upcoming events")
                            .font(.system(size: 12, weight: .semibold))
                            .foregroundStyle(.white.opacity(0.75))
                    }
                }

                calendarPrimaryButton(title: "Open Calendar", systemImage: "calendar") {
                    openCalendarPath("/calendar/u/0/r")
                }
            }
            .padding(14)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        .disabled(isPreview)
        .opacity(isPreview ? 0.88 : 1)
    }

    private var todayDateBlock: some View {
        let now = Date()
        let cal = Calendar.current
        let day = cal.component(.day, from: now)
        let weekday = Self.weekdayFormatter.string(from: now).uppercased()
        let monthYear = Self.monthYearFormatter.string(from: now)

        return HStack(alignment: .firstTextBaseline, spacing: 14) {
            Text("\(day)")
                .font(.system(size: 36, weight: .bold, design: .rounded))
                .foregroundStyle(.white)
            VStack(alignment: .leading, spacing: 2) {
                Text(weekday)
                    .font(.system(size: 10, weight: .bold))
                    .foregroundStyle(.white.opacity(0.55))
                Text(monthYear)
                    .font(.system(size: 14, weight: .semibold))
                    .foregroundStyle(.white.opacity(0.9))
            }
            Spacer(minLength: 0)
        }
    }

    private func nextEventSummary(_ ev: CalendarEventSnapshot) -> some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(ev.title.isEmpty ? "(No title)" : ev.title)
                .font(.system(size: 13, weight: .bold))
                .foregroundStyle(.white.opacity(0.95))
                .lineLimit(2)
            Text(Self.eventTimeRange(ev))
                .font(.system(size: 11, weight: .medium))
                .foregroundStyle(.white.opacity(0.65))
            if let loc = ev.location, !loc.isEmpty {
                Text(loc)
                    .font(.system(size: 10, weight: .medium))
                    .foregroundStyle(.white.opacity(0.5))
                    .lineLimit(1)
            }
        }
        .padding(10)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(RoundedRectangle(cornerRadius: 12, style: .continuous).fill(Color.black.opacity(0.22)))
    }

    // MARK: - Medium

    private var agendaBody: some View {
        ZStack(alignment: .topLeading) {
            calendarGradientBackground
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            VStack(alignment: .leading, spacing: 0) {
                HStack(spacing: 10) {
                    AppIconView(app: app, size: 30, showAppBackground: false, iconPaddingFactor: 0.08)
                    VStack(alignment: .leading, spacing: 2) {
                        Text(app.name)
                            .font(.system(size: 14, weight: .bold))
                            .foregroundStyle(.white.opacity(0.95))
                        Text("Agenda")
                            .font(.system(size: 10, weight: .semibold))
                            .foregroundStyle(.white.opacity(0.65))
                    }
                    Spacer(minLength: 0)
                    if isPreview {
                        Text("Sample")
                            .font(.system(size: 10, weight: .bold))
                            .foregroundStyle(.white.opacity(0.5))
                            .padding(.horizontal, 8)
                            .padding(.vertical, 4)
                            .background(Capsule().fill(Color.white.opacity(0.12)))
                    }
                }
                .padding(.bottom, 10)

                quickAddRow
                    .padding(.bottom, 12)

                if model.accessDenied && !isPreview {
                    Text("Allow Calendar access in System Settings to show your events.")
                        .font(.system(size: 12, weight: .medium))
                        .foregroundStyle(.white.opacity(0.75))
                    Button(action: openPrivacyCalendars) {
                        Text("Privacy…")
                            .font(.system(size: 12, weight: .bold))
                    }
                    .buttonStyle(.plain)
                    .foregroundStyle(Color(red: 0.75, green: 0.9, blue: 1))
                    .padding(.top, 4)
                    Spacer(minLength: 0)
                } else {
                    ScrollView {
                        LazyVStack(alignment: .leading, spacing: 16) {
                            ForEach(model.agendaDays) { day in
                                agendaDaySection(day)
                            }
                            if model.agendaDays.isEmpty {
                                Text(isPreview ? "Sample events appear here." : "No events in the next several days.")
                                    .font(.system(size: 12, weight: .medium))
                                    .foregroundStyle(.white.opacity(0.6))
                                    .padding(.top, 8)
                            }
                        }
                        .padding(.bottom, 8)
                    }
                }
            }
            .padding(14)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        .disabled(isPreview)
        .opacity(isPreview ? 0.88 : 1)
    }

    private var quickAddRow: some View {
        HStack(spacing: 8) {
            quickChip(title: "New event", systemImage: "plus.circle.fill") {
                openCalendarPath("/calendar/u/0/r/eventedit")
            }
            quickChip(title: "Tasks", systemImage: "checkmark.square") {
                openAbsoluteURL(URL(string: "https://tasks.google.com/")!)
            }
            quickChip(title: "Week", systemImage: "calendar.badge.clock") {
                openCalendarPath("/calendar/u/0/r/week")
            }
            Spacer(minLength: 0)
        }
    }

    private func quickChip(title: String, systemImage: String, action: @escaping () -> Void) -> some View {
        Button(action: {
            NSApp.activate(ignoringOtherApps: true)
            action()
        }) {
            HStack(spacing: 6) {
                Image(systemName: systemImage)
                    .font(.system(size: 11, weight: .semibold))
                Text(title)
                    .font(.system(size: 11, weight: .bold))
                    .lineLimit(1)
            }
            .foregroundStyle(.white.opacity(0.92))
            .padding(.horizontal, 10)
            .padding(.vertical, 8)
            .background(
                RoundedRectangle(cornerRadius: 10, style: .continuous)
                    .fill(Color.black.opacity(0.28))
            )
            .overlay(
                RoundedRectangle(cornerRadius: 10, style: .continuous)
                    .stroke(Color.white.opacity(0.14), lineWidth: 1)
            )
        }
        .buttonStyle(.plain)
        .disabled(isPreview)
        .allowsHitTesting(!isPreview)
    }

    private func agendaDaySection(_ day: CalendarAgendaDay) -> some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(day.dayLabel)
                .font(.system(size: 11, weight: .bold))
                .foregroundStyle(.white.opacity(0.45))

            ForEach(day.events) { ev in
                VStack(alignment: .leading, spacing: 4) {
                    HStack(alignment: .firstTextBaseline) {
                        Text(Self.eventTimeRange(ev))
                            .font(.system(size: 11, weight: .bold))
                            .foregroundStyle(Color(red: 0.75, green: 0.92, blue: 0.85))
                            .frame(width: 108, alignment: .leading)
                        Text(ev.title.isEmpty ? "(No title)" : ev.title)
                            .font(.system(size: 12, weight: .semibold))
                            .foregroundStyle(.white.opacity(0.95))
                            .lineLimit(2)
                    }
                    if let loc = ev.location, !loc.isEmpty {
                        HStack(spacing: 4) {
                            Image(systemName: "mappin.and.ellipse")
                                .font(.system(size: 9, weight: .semibold))
                            Text(loc)
                                .font(.system(size: 10, weight: .medium))
                                .lineLimit(2)
                        }
                        .foregroundStyle(.white.opacity(0.45))
                        .padding(.leading, 108)
                    }
                }
                .padding(10)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(RoundedRectangle(cornerRadius: 12, style: .continuous).fill(Color.black.opacity(0.2)))
            }
        }
    }

    // MARK: - Chrome

    private var calendarGradientBackground: some View {
        LinearGradient(
            colors: [
                Color(red: 0.16, green: 0.38, blue: 0.55),
                Color(red: 0.1, green: 0.14, blue: 0.28),
            ],
            startPoint: .topLeading,
            endPoint: .bottomTrailing
        )
    }

    private func calendarPrimaryButton(title: String, systemImage: String, action: @escaping () -> Void) -> some View {
        Button(action: {
            NSApp.activate(ignoringOtherApps: true)
            action()
        }) {
            HStack(spacing: 8) {
                Image(systemName: systemImage)
                    .font(.system(size: 13, weight: .semibold))
                Text(title)
                    .font(.system(size: 13, weight: .bold))
            }
            .foregroundStyle(Color(red: 0.06, green: 0.12, blue: 0.18))
            .frame(maxWidth: .infinity)
            .padding(.vertical, 11)
            .background(
                RoundedRectangle(cornerRadius: 12, style: .continuous)
                    .fill(Color.white.opacity(0.9))
            )
        }
        .buttonStyle(.plain)
        .disabled(isPreview)
        .allowsHitTesting(!isPreview)
    }

    private func openCalendarPath(_ path: String) {
        guard !isPreview else { return }
        let url = WidgetDeepLinkURL.resolved(base: baseURL, actionPath: path, accountSlot: 0)
        InAppBrowserWindowManager.shared.open(url: url, title: app.name)
    }

    private func openAbsoluteURL(_ url: URL) {
        guard !isPreview else { return }
        InAppBrowserWindowManager.shared.open(url: url, title: app.name)
    }

    private func openPrivacyCalendars() {
        guard !isPreview else { return }
        let s = "x-apple.systempreferences:com.apple.preference.security?Privacy_Calendars"
        if let url = URL(string: s) {
            NSWorkspace.shared.open(url)
        }
    }

    private static let weekdayFormatter: DateFormatter = {
        let f = DateFormatter()
        f.dateFormat = "EEEE"
        return f
    }()

    private static let monthYearFormatter: DateFormatter = {
        let f = DateFormatter()
        f.dateFormat = "MMMM yyyy"
        return f
    }()

    private static func eventTimeRange(_ ev: CalendarEventSnapshot) -> String {
        if ev.isAllDay {
            return "All day"
        }
        let t = DateFormatter()
        t.timeStyle = .short
        t.dateStyle = .none
        let start = t.string(from: ev.start)
        let end = t.string(from: ev.end)
        return "\(start) – \(end)"
    }
}

// MARK: - EventKit

@MainActor
private final class CalendarWidgetEventsModel: ObservableObject {
    @Published private(set) var nextEvent: CalendarEventSnapshot?
    @Published private(set) var agendaDays: [CalendarAgendaDay] = []
    @Published private(set) var accessDenied = false

    private let store = EKEventStore()

    func refresh(isPreview: Bool) async {
        if isPreview {
            loadSample()
            accessDenied = false
            return
        }

        let status = EKEventStore.authorizationStatus(for: .event)
        if Self.canReadEvents(status) {
            accessDenied = false
            fetchAndPublish()
            return
        }

        if status == .denied || status == .restricted {
            accessDenied = true
            nextEvent = nil
            agendaDays = []
            return
        }

        let granted = await requestAccess()
        accessDenied = !granted
        if granted {
            fetchAndPublish()
        } else {
            nextEvent = nil
            agendaDays = []
        }
    }

    private func requestAccess() async -> Bool {
        await withCheckedContinuation { continuation in
            if #available(macOS 14.0, *) {
                store.requestFullAccessToEvents { granted, _ in
                    continuation.resume(returning: granted)
                }
            } else {
                store.requestAccess(to: .event) { granted, _ in
                    continuation.resume(returning: granted)
                }
            }
        }
    }

    private func fetchAndPublish() {
        let now = Date()
        let cal = Calendar.current
        let startOfToday = cal.startOfDay(for: now)
        guard let rangeEnd = cal.date(byAdding: .day, value: 7, to: startOfToday) else {
            nextEvent = nil
            agendaDays = []
            return
        }

        let predicate = store.predicateForEvents(withStart: startOfToday, end: rangeEnd, calendars: nil)
        let events = store.events(matching: predicate).sorted { $0.startDate < $1.startDate }

        let snapshots: [CalendarEventSnapshot] = events.map { e in
            CalendarEventSnapshot(
                id: e.eventIdentifier,
                title: e.title ?? "",
                start: e.startDate,
                end: e.endDate,
                isAllDay: e.isAllDay,
                location: e.location
            )
        }

        let upcoming = snapshots.filter { $0.end > now }
        nextEvent = upcoming.first

        var byDay: [Date: [CalendarEventSnapshot]] = [:]
        for ev in upcoming {
            let dayStart = cal.startOfDay(for: ev.start)
            byDay[dayStart, default: []].append(ev)
        }

        let sortedDays = byDay.keys.sorted()
        agendaDays = sortedDays.map { day in
            let label = Self.dayHeading(for: day, calendar: cal)
            let id = "\(day.timeIntervalSince1970)"
            let dayEvents = (byDay[day] ?? []).sorted { $0.start < $1.start }
            return CalendarAgendaDay(id: id, dayLabel: label, events: dayEvents)
        }
    }

    private func loadSample() {
        let cal = Calendar.current
        let now = Date()
        let later = cal.date(byAdding: .hour, value: 2, to: now) ?? now
        let end = cal.date(byAdding: .hour, value: 3, to: now) ?? now

        let s1 = CalendarEventSnapshot(id: "s1", title: "Product review", start: later, end: end, isAllDay: false, location: "Building A")
        nextEvent = s1

        let tomorrow = cal.date(byAdding: .day, value: 1, to: cal.startOfDay(for: now)) ?? now
        let tStart = cal.date(byAdding: .hour, value: 10, to: tomorrow) ?? tomorrow
        let tEnd = cal.date(byAdding: .hour, value: 11, to: tomorrow) ?? tomorrow
        let s2 = CalendarEventSnapshot(id: "s2", title: "Design sync", start: tStart, end: tEnd, isAllDay: false, location: nil)

        agendaDays = [
            CalendarAgendaDay(
                id: "d0",
                dayLabel: "Today",
                events: [s1]
            ),
            CalendarAgendaDay(
                id: "d1",
                dayLabel: "Tomorrow",
                events: [s2]
            ),
        ]
    }

    private static func dayHeading(for dayStart: Date, calendar: Calendar) -> String {
        if calendar.isDateInToday(dayStart) { return "Today" }
        if calendar.isDateInTomorrow(dayStart) { return "Tomorrow" }
        let f = DateFormatter()
        f.dateFormat = "EEEE, MMM d"
        return f.string(from: dayStart)
    }

    private static func canReadEvents(_ status: EKAuthorizationStatus) -> Bool {
        switch status {
        case .authorized:
            return true
        default:
            if #available(macOS 14.0, *) {
                return status == .fullAccess
            }
            return false
        }
    }
}
