import AppKit
import Combine
import EventKit
import SwiftUI

struct MeetMeetingSnapshot: Identifiable, Hashable {
    let id: String
    let title: String
    let start: Date
    let end: Date
    let meetURL: URL
    let location: String?
}

/// Google Meet widgets: calendar-backed Meet links from Apple Calendar (events with `meet.google.com` in URL, location, or notes).
struct MeetDesktopWidgetView: View {
    let app: LauncherApp
    let mode: MeetWidgetMode
    var isPreview: Bool = false

    @StateObject private var model = MeetWidgetEventsModel()

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

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

    // MARK: - Small — quick join

    private var compactBody: some View {
        ZStack(alignment: .topLeading) {
            meetGradientBackground
            VStack(alignment: .leading, spacing: 10) {
                HStack(spacing: 8) {
                    AppIconView(app: app, size: 30, showAppBackground: false, iconPaddingFactor: 0.08)
                    Text("Meet")
                        .font(.system(size: 14, weight: .bold))
                        .foregroundStyle(.white.opacity(0.95))
                    Spacer(minLength: 0)
                }

                if model.accessDenied && !isPreview {
                    Text("Calendar access is off. Enable Calendar in System Settings to see meetings with Meet links.")
                        .font(.system(size: 11, weight: .medium))
                        .foregroundStyle(.white.opacity(0.75))
                        .fixedSize(horizontal: false, vertical: true)
                    Button("Open Privacy settings", action: openPrivacyCalendars)
                        .font(.system(size: 11, weight: .bold))
                        .buttonStyle(.plain)
                        .foregroundStyle(Color(red: 0.75, green: 0.95, blue: 0.85))
                } else if let next = model.nextMeet {
                    Text("Next")
                        .font(.system(size: 10, weight: .bold))
                        .foregroundStyle(.white.opacity(0.5))
                    Text(next.title)
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(.white)
                        .lineLimit(2)
                    Text(Self.formatStart(next.start, relativeTo: next.end))
                        .font(.system(size: 11, weight: .medium))
                        .foregroundStyle(.white.opacity(0.75))
                    meetPrimaryButton(title: "Join", systemImage: "video.fill") {
                        openMeetURL(next.meetURL)
                    }
                } else if let last = model.lastMeet {
                    Text("Last meeting")
                        .font(.system(size: 10, weight: .bold))
                        .foregroundStyle(.white.opacity(0.5))
                    Text(last.title)
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(.white.opacity(0.9))
                        .lineLimit(2)
                    Text("Ended \(Self.timeFormatter.string(from: last.end))")
                        .font(.system(size: 11, weight: .medium))
                        .foregroundStyle(.white.opacity(0.65))
                    meetPrimaryButton(title: "Open link", systemImage: "link") {
                        openMeetURL(last.meetURL)
                    }
                } else {
                    Text(isPreview ? "Sample: sync at 3:00 PM" : "No Meet links in Calendar")
                        .font(.system(size: 12, weight: .medium))
                        .foregroundStyle(.white.opacity(0.75))
                    meetPrimaryButton(title: "New meeting", systemImage: "video.badge.plus") {
                        openMeetPath("/new")
                    }
                }
            }
            .padding(14)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        }
        .disabled(isPreview)
        .opacity(isPreview ? 0.88 : 1)
    }

    // MARK: - Medium — upcoming list

    private var agendaBody: some View {
        ZStack(alignment: .topLeading) {
            meetGradientBackground
            VStack(alignment: .leading, spacing: 10) {
                HStack(spacing: 8) {
                    AppIconView(app: app, size: 28, showAppBackground: false, iconPaddingFactor: 0.08)
                    Text("Upcoming")
                        .font(.system(size: 14, weight: .bold))
                        .foregroundStyle(.white.opacity(0.95))
                    Spacer(minLength: 0)
                    Button(action: { openMeetPath("/new") }) {
                        Image(systemName: "plus.circle.fill")
                            .font(.system(size: 20, weight: .semibold))
                            .foregroundStyle(Self.accentGreen)
                    }
                    .buttonStyle(.plain)
                    .help("New meeting")
                }

                if model.accessDenied && !isPreview {
                    Text("Turn on Calendar access in System Settings to list Meet meetings.")
                        .font(.system(size: 11, weight: .medium))
                        .foregroundStyle(.white.opacity(0.75))
                    Button("Open Privacy settings", action: openPrivacyCalendars)
                        .font(.system(size: 11, weight: .bold))
                        .buttonStyle(.plain)
                        .foregroundStyle(Color(red: 0.75, green: 0.95, blue: 0.85))
                } else if model.upcomingMeets.isEmpty {
                    Text(isPreview ? "Design review · 2:00 PM" : "No upcoming meetings with Meet links")
                        .font(.system(size: 12, weight: .medium))
                        .foregroundStyle(.white.opacity(0.8))
                    meetPrimaryButton(title: "New meeting", systemImage: "video.badge.plus") {
                        openMeetPath("/new")
                    }
                } else {
                    ScrollView {
                        VStack(alignment: .leading, spacing: 8) {
                            ForEach(model.upcomingMeets) { ev in
                                agendaRow(ev)
                            }
                        }
                    }
                    .frame(maxHeight: .infinity)
                }
            }
            .padding(14)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        }
        .disabled(isPreview)
        .opacity(isPreview ? 0.88 : 1)
    }

    private func agendaRow(_ ev: MeetMeetingSnapshot) -> some View {
        HStack(alignment: .center, spacing: 10) {
            VStack(alignment: .leading, spacing: 3) {
                Text(ev.title)
                    .font(.system(size: 12, weight: .semibold))
                    .foregroundStyle(.white)
                    .lineLimit(2)
                Text(Self.formatStart(ev.start, relativeTo: ev.end))
                    .font(.system(size: 10, weight: .medium))
                    .foregroundStyle(.white.opacity(0.65))
            }
            Spacer(minLength: 0)
            Button(action: { openMeetURL(ev.meetURL) }) {
                Text("Join")
                    .font(.system(size: 11, weight: .bold))
                    .foregroundStyle(.white)
                    .padding(.horizontal, 12)
                    .padding(.vertical, 6)
                    .background(Capsule().fill(Self.accentGreen.opacity(0.95)))
            }
            .buttonStyle(.plain)
        }
        .padding(10)
        .background(
            RoundedRectangle(cornerRadius: 12, style: .continuous)
                .fill(Color.white.opacity(0.08))
        )
    }

    // MARK: - Large — hub

    private var hubBody: some View {
        ZStack(alignment: .topLeading) {
            meetGradientBackground
            ScrollView {
                VStack(alignment: .leading, spacing: 14) {
                    HStack(spacing: 8) {
                        AppIconView(app: app, size: 30, showAppBackground: false, iconPaddingFactor: 0.08)
                        VStack(alignment: .leading, spacing: 2) {
                            Text("Google Meet")
                                .font(.system(size: 15, weight: .bold))
                                .foregroundStyle(.white)
                            Text("From your Calendar")
                                .font(.system(size: 10, weight: .semibold))
                                .foregroundStyle(.white.opacity(0.55))
                        }
                        Spacer(minLength: 0)
                        Button(action: { openMeetPath("/") }) {
                            Image(systemName: "safari")
                                .font(.system(size: 15, weight: .semibold))
                                .foregroundStyle(.white.opacity(0.9))
                                .padding(8)
                                .background(Circle().fill(Color.white.opacity(0.12)))
                        }
                        .buttonStyle(.plain)
                        .help("Open Meet in browser")
                    }

                    if model.accessDenied && !isPreview {
                        Text("Calendar access is required to show meetings. Enable it under Privacy & Security → Calendars.")
                            .font(.system(size: 11, weight: .medium))
                            .foregroundStyle(.white.opacity(0.75))
                        Button("Open Privacy settings", action: openPrivacyCalendars)
                            .font(.system(size: 11, weight: .bold))
                            .buttonStyle(.plain)
                            .foregroundStyle(Color(red: 0.75, green: 0.95, blue: 0.85))
                    } else {
                        HStack(spacing: 10) {
                            if let next = model.nextMeet {
                                meetPrimaryButton(title: "Join next", systemImage: "video.fill") {
                                    openMeetURL(next.meetURL)
                                }
                            } else {
                                meetPrimaryButton(title: "New meeting", systemImage: "video.badge.plus") {
                                    openMeetPath("/new")
                                }
                            }
                            Button(action: { openMeetPath("/new") }) {
                                VStack(spacing: 4) {
                                    Image(systemName: "plus.circle.fill")
                                        .font(.system(size: 22, weight: .semibold))
                                    Text("Create")
                                        .font(.system(size: 10, weight: .bold))
                                }
                                .foregroundStyle(.white)
                                .frame(maxWidth: .infinity)
                                .padding(.vertical, 12)
                                .background(
                                    RoundedRectangle(cornerRadius: 14, style: .continuous)
                                        .fill(Color.white.opacity(0.12))
                                )
                            }
                            .buttonStyle(.plain)
                        }

                        if let next = model.nextMeet {
                            hubSectionTitle("Next meeting details")
                            meetDetailCard(next)
                        }

                        hubSectionTitle("Scheduled")
                        if model.upcomingMeets.isEmpty {
                            Text(isPreview ? "Weekly sync · Today 3:00 PM" : "No upcoming Meet events")
                                .font(.system(size: 11, weight: .medium))
                                .foregroundStyle(.white.opacity(0.65))
                        } else {
                            ForEach(model.upcomingMeets) { ev in
                                hubScheduledRow(ev)
                            }
                        }

                        hubSectionTitle("Recent")
                        if model.recentMeets.isEmpty {
                            Text("No recent Meet links")
                                .font(.system(size: 11, weight: .medium))
                                .foregroundStyle(.white.opacity(0.55))
                        } else {
                            ForEach(model.recentMeets) { ev in
                                hubRecentRow(ev)
                            }
                        }

                        hubSectionTitle("Shortcuts")
                        HStack(spacing: 8) {
                            hubShortcut(title: "Calendar", systemImage: "calendar", action: {
                                if let u = URL(string: "https://calendar.google.com/calendar/u/0/r") {
                                    InAppBrowserWindowManager.shared.open(url: u, title: "Google Calendar")
                                }
                            })
                            hubShortcut(title: "Join code", systemImage: "keyboard", action: {
                                openMeetPath("/")
                            })
                        }
                    }
                }
                .padding(14)
            }
        }
        .disabled(isPreview)
        .opacity(isPreview ? 0.88 : 1)
    }

    private func hubSectionTitle(_ text: String) -> some View {
        Text(text)
            .font(.system(size: 10, weight: .bold))
            .foregroundStyle(.white.opacity(0.45))
            .textCase(.uppercase)
    }

    private func meetDetailCard(_ ev: MeetMeetingSnapshot) -> some View {
        VStack(alignment: .leading, spacing: 6) {
            Text(ev.title)
                .font(.system(size: 14, weight: .bold))
                .foregroundStyle(.white)
            Text(Self.formatRange(ev.start, ev.end))
                .font(.system(size: 12, weight: .medium))
                .foregroundStyle(.white.opacity(0.75))
            if let loc = ev.location, !loc.isEmpty {
                Text(loc)
                    .font(.system(size: 11, weight: .medium))
                    .foregroundStyle(.white.opacity(0.55))
                    .lineLimit(2)
            }
            Button(action: { openMeetURL(ev.meetURL) }) {
                Label("Join with Google Meet", systemImage: "video.fill")
                    .font(.system(size: 12, weight: .bold))
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 10)
                    .background(RoundedRectangle(cornerRadius: 12, style: .continuous).fill(Self.accentGreen))
            }
            .buttonStyle(.plain)
            .foregroundStyle(.white)
        }
        .padding(12)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(
            RoundedRectangle(cornerRadius: 16, style: .continuous)
                .fill(Color.white.opacity(0.08))
        )
    }

    private func hubScheduledRow(_ ev: MeetMeetingSnapshot) -> some View {
        HStack {
            VStack(alignment: .leading, spacing: 3) {
                Text(ev.title)
                    .font(.system(size: 12, weight: .semibold))
                    .foregroundStyle(.white)
                Text(Self.formatStart(ev.start, relativeTo: ev.end))
                    .font(.system(size: 10, weight: .medium))
                    .foregroundStyle(.white.opacity(0.6))
            }
            Spacer(minLength: 0)
            Button("Join") { openMeetURL(ev.meetURL) }
                .font(.system(size: 11, weight: .bold))
                .buttonStyle(.borderedProminent)
                .tint(Self.accentGreen)
                .controlSize(.small)
        }
        .padding(10)
        .background(RoundedRectangle(cornerRadius: 12, style: .continuous).fill(Color.white.opacity(0.06)))
    }

    private func hubRecentRow(_ ev: MeetMeetingSnapshot) -> some View {
        HStack {
            VStack(alignment: .leading, spacing: 3) {
                Text(ev.title)
                    .font(.system(size: 11, weight: .semibold))
                    .foregroundStyle(.white.opacity(0.85))
                Text("Ended \(Self.timeFormatter.string(from: ev.end))")
                    .font(.system(size: 10, weight: .medium))
                    .foregroundStyle(.white.opacity(0.5))
            }
            Spacer(minLength: 0)
            Button(action: { openMeetURL(ev.meetURL) }) {
                Image(systemName: "arrow.up.right.square")
                    .font(.system(size: 14, weight: .semibold))
                    .foregroundStyle(.white.opacity(0.8))
            }
            .buttonStyle(.plain)
            .help("Open Meet link")
        }
        .padding(10)
        .background(RoundedRectangle(cornerRadius: 12, style: .continuous).fill(Color.white.opacity(0.05)))
    }

    private func hubShortcut(title: String, systemImage: String, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            HStack(spacing: 6) {
                Image(systemName: systemImage)
                    .font(.system(size: 13, weight: .semibold))
                Text(title)
                    .font(.system(size: 11, weight: .bold))
            }
            .foregroundStyle(.white.opacity(0.9))
            .frame(maxWidth: .infinity)
            .padding(.vertical, 10)
            .background(RoundedRectangle(cornerRadius: 12, style: .continuous).fill(Color.white.opacity(0.1)))
        }
        .buttonStyle(.plain)
    }

    private var meetGradientBackground: some View {
        LinearGradient(
            colors: [
                Color(red: 0.05, green: 0.14, blue: 0.12),
                Color(red: 0.07, green: 0.09, blue: 0.1),
            ],
            startPoint: .topLeading,
            endPoint: .bottomTrailing
        )
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

    private func meetPrimaryButton(title: String, systemImage: String, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            HStack(spacing: 8) {
                Image(systemName: systemImage)
                    .font(.system(size: 14, weight: .semibold))
                Text(title)
                    .font(.system(size: 13, weight: .bold))
            }
            .foregroundStyle(.white)
            .frame(maxWidth: .infinity)
            .padding(.vertical, 11)
            .background(
                RoundedRectangle(cornerRadius: 14, style: .continuous)
                    .fill(Self.accentGreen)
            )
        }
        .buttonStyle(.plain)
    }

    private static let accentGreen = Color(red: 0.16, green: 0.65, blue: 0.48)

    private static let timeFormatter: DateFormatter = {
        let f = DateFormatter()
        f.timeStyle = .short
        f.dateStyle = .none
        return f
    }()

    private static let dayFormatter: DateFormatter = {
        let f = DateFormatter()
        f.dateFormat = "EEE, MMM d"
        return f
    }()

    private static func formatStart(_ start: Date, relativeTo end: Date) -> String {
        let cal = Calendar.current
        if cal.isDateInToday(start) {
            return "Today · \(timeFormatter.string(from: start)) – \(timeFormatter.string(from: end))"
        }
        if cal.isDateInTomorrow(start) {
            return "Tomorrow · \(timeFormatter.string(from: start))"
        }
        return "\(dayFormatter.string(from: start)) · \(timeFormatter.string(from: start))"
    }

    private static func formatRange(_ start: Date, _ end: Date) -> String {
        "\(timeFormatter.string(from: start)) – \(timeFormatter.string(from: end))"
    }

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

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

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

// MARK: - EventKit

@MainActor
private final class MeetWidgetEventsModel: ObservableObject {
    @Published private(set) var nextMeet: MeetMeetingSnapshot?
    @Published private(set) var lastMeet: MeetMeetingSnapshot?
    @Published private(set) var upcomingMeets: [MeetMeetingSnapshot] = []
    @Published private(set) var recentMeets: [MeetMeetingSnapshot] = []
    @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
            clear()
            return
        }

        let granted = await requestAccess()
        accessDenied = !granted
        if granted {
            fetchAndPublish()
        } else {
            clear()
        }
    }

    private func clear() {
        nextMeet = nil
        lastMeet = nil
        upcomingMeets = []
        recentMeets = []
    }

    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
        guard let start = cal.date(byAdding: .day, value: -2, to: cal.startOfDay(for: now)),
              let end = cal.date(byAdding: .day, value: 14, to: now) else {
            clear()
            return
        }

        let predicate = store.predicateForEvents(withStart: start, end: end, calendars: nil)
        let events = store.events(matching: predicate)
        let snapshots: [MeetMeetingSnapshot] = events.compactMap { Self.snapshot(from: $0) }

        let upcoming = snapshots.filter { $0.end > now }.sorted { $0.start < $1.start }
        nextMeet = upcoming.first
        upcomingMeets = Array(upcoming.prefix(8))

        let past = snapshots.filter { $0.end <= now }.sorted { $0.start > $1.start }
        recentMeets = Array(past.prefix(6))
        lastMeet = past.first
    }

    private static func snapshot(from event: EKEvent) -> MeetMeetingSnapshot? {
        guard let url = extractMeetURL(from: event) else { return nil }
        let raw = event.title?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        let title = raw.isEmpty ? "Meeting" : raw
        return MeetMeetingSnapshot(
            id: event.eventIdentifier,
            title: title,
            start: event.startDate,
            end: event.endDate,
            meetURL: url,
            location: event.location
        )
    }

    private static func extractMeetURL(from event: EKEvent) -> URL? {
        let combined = [event.url?.absoluteString, event.location, event.notes]
            .compactMap { $0 }
            .joined(separator: "\n")
        guard !combined.isEmpty else { return nil }
        guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { return nil }
        let range = NSRange(combined.startIndex..., in: combined)
        for match in detector.matches(in: combined, options: [], range: range) {
            guard let url = match.url, let host = url.host?.lowercased(), host.contains("meet.google.com") else { continue }
            return url
        }
        return nil
    }

    private func loadSample() {
        let cal = Calendar.current
        let now = Date()
        let soon = cal.date(byAdding: .hour, value: 1, to: now) ?? now
        let soonEnd = cal.date(byAdding: .minute, value: 45, to: soon) ?? soon
        let u = URL(string: "https://meet.google.com/abc-defg-hij")!

        let n = MeetMeetingSnapshot(
            id: "sample-next",
            title: "Weekly sync",
            start: soon,
            end: soonEnd,
            meetURL: u,
            location: "Calendar"
        )
        nextMeet = n
        upcomingMeets = [n]

        let pastEnd = cal.date(byAdding: .hour, value: -1, to: now) ?? now
        let pastStart = cal.date(byAdding: .hour, value: -2, to: now) ?? now
        let last = MeetMeetingSnapshot(
            id: "sample-last",
            title: "Stand-up",
            start: pastStart,
            end: pastEnd,
            meetURL: URL(string: "https://meet.google.com/zzz-yyyy-xxx")!,
            location: nil
        )
        lastMeet = last
        recentMeets = [last]
    }

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