import ForgejoKit
import UIKit
import UserNotifications

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _: UIApplication,
        didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil,
    ) -> Bool {
        NotificationPollingService.shared.registerBackgroundTask()
        BackgroundDownloadManager.shared.reconnectIfNeeded()

        let center = UNUserNotificationCenter.current()
        center.delegate = self

        let markReadAction = UNNotificationAction(
            identifier: "MARK_READ",
            title: "Mark as Read",
            options: [],
        )
        let category = UNNotificationCategory(
            identifier: "FORGEJO_NOTIFICATION",
            actions: [markReadAction],
            intentIdentifiers: [],
        )
        center.setNotificationCategories([category])

        return true
    }

    func application(
        _: UIApplication,
        handleEventsForBackgroundURLSession _: String,
        completionHandler: @escaping () -> Void,
    ) {
        BackgroundDownloadManager.shared.setCompletionHandler(completionHandler)
    }
}

// MARK: - UNUserNotificationCenterDelegate

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(
        _: UNUserNotificationCenter,
        willPresent _: UNNotification,
    ) async -> UNNotificationPresentationOptions {
        [.banner, .sound, .badge]
    }

    func userNotificationCenter(
        _: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
    ) async {
        let userInfo = response.notification.request.content.userInfo
        guard let serverURL = userInfo["serverURL"] as? String,
              let username = userInfo["username"] as? String
        else { return }

        if response.actionIdentifier == "MARK_READ" {
            await markNotificationAsRead(userInfo: userInfo, serverURL: serverURL, username: username)
        } else {
            // Opening the notification clears it, the same as tapping it inside the app.
            await markNotificationAsRead(userInfo: userInfo, serverURL: serverURL, username: username)
            await MainActor.run {
                NavigationState.shared.navigateToNotifications(serverURL: serverURL, username: username)
            }
        }
    }

    private func markNotificationAsRead(userInfo: [AnyHashable: Any], serverURL: String, username: String) async {
        guard let notificationID = userInfo["notificationID"] as? Int,
              let token = try? await KeychainManager.shared.getToken(for: serverURL, username: username)
        else { return }

        let client = ForgejoClient(serverURL: serverURL, username: username, token: token)
        let service = NotificationService(client: client)
        try? await service.markAsRead(id: notificationID)
    }
}
