import UserNotifications
import Intents

/// Notification Service Extension — runs as a separate process even when the main app
/// is terminated. Intercepts push notifications with `mutable-content: 1` and:
/// 1. Adds `.default` sound for vibration (server payload has no sound)
/// 2. Increments the app icon badge from shared App Group storage
/// 3. Normalizes sender_public_key in userInfo (Android parity: multi-key fallback)
/// 4. Filters muted chats
/// 5. Creates Communication Notification via INSendMessageIntent (CarPlay + Focus parity)
final class NotificationService: UNNotificationServiceExtension {

    private static let appGroupID = "group.com.rosetta.dev"
    private static let badgeKey = "app_badge_count"

    /// Android parity: multiple key names for sender public key extraction.
    private static let senderKeyNames = [
        "sender_public_key", "from_public_key", "fromPublicKey",
        "public_key", "publicKey"
    ]
    private static let senderNameKeyNames = [
        "sender_name", "from_title", "sender", "title", "name"
    ]

    private var contentHandler: ((UNNotificationContent) -> Void)?
    private var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        self.contentHandler = contentHandler
        bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent

        guard let content = bestAttemptContent else {
            contentHandler(request.content)
            return
        }

        // 1. Add sound for vibration — server APNs payload has no sound field.
        content.sound = .default

        // 2. Increment badge count from shared App Group storage.
        if let shared = UserDefaults(suiteName: Self.appGroupID) {
            let current = shared.integer(forKey: Self.badgeKey)
            let newBadge = current + 1
            shared.set(newBadge, forKey: Self.badgeKey)
            content.badge = NSNumber(value: newBadge)

            // 4. Filter muted chats.
            let senderKey = Self.extractSenderKey(from: content.userInfo)
            let mutedKeys = shared.stringArray(forKey: "muted_chats_keys") ?? []
            if !senderKey.isEmpty, mutedKeys.contains(senderKey) {
                // Muted: deliver silently (no sound, no alert)
                content.sound = nil
                content.title = ""
                content.body = ""
                contentHandler(content)
                return
            }
        }

        // 3. Normalize sender_public_key in userInfo for tap navigation.
        // Server may send under different key names — normalize to "sender_public_key".
        let senderKey = Self.extractSenderKey(from: content.userInfo)
        if !senderKey.isEmpty {
            var updatedInfo = content.userInfo
            updatedInfo["sender_public_key"] = senderKey
            // Also normalize sender_name
            if let name = Self.firstNonBlank(content.userInfo, keys: Self.senderNameKeyNames) {
                updatedInfo["sender_name"] = name
            }
            content.userInfo = updatedInfo
        }

        // 5. Ensure notification category for CarPlay parity.
        if content.categoryIdentifier.isEmpty {
            content.categoryIdentifier = "message"
        }

        // 6. Create Communication Notification via INSendMessageIntent.
        // This makes the notification appear on CarPlay and work with Focus filters.
        // Apple requires INSendMessageIntent for messaging notifications on CarPlay (iOS 15+).
        let senderName = Self.firstNonBlank(content.userInfo, keys: Self.senderNameKeyNames)
            ?? content.title
        let finalContent = Self.makeCommunicationNotification(
            content: content,
            senderName: senderName,
            senderKey: senderKey
        )

        contentHandler(finalContent)
    }

    override func serviceExtensionTimeWillExpire() {
        if let handler = contentHandler, let content = bestAttemptContent {
            content.sound = .default
            handler(content)
        }
    }

    // MARK: - Communication Notification (CarPlay + Focus)

    /// Wraps the notification content with an INSendMessageIntent so iOS treats it
    /// as a Communication Notification. This enables:
    /// - Display on CarPlay
    /// - Proper grouping in Focus modes
    /// - Sender name/avatar in notification UI
    private static func makeCommunicationNotification(
        content: UNMutableNotificationContent,
        senderName: String,
        senderKey: String
    ) -> UNNotificationContent {
        let handle = INPersonHandle(value: senderKey, type: .unknown)
        let sender = INPerson(
            personHandle: handle,
            nameComponents: nil,
            displayName: senderName.isEmpty ? "Rosetta" : senderName,
            image: nil,
            contactIdentifier: nil,
            customIdentifier: senderKey
        )

        let intent = INSendMessageIntent(
            recipients: nil,
            outgoingMessageType: .outgoingMessageText,
            content: content.body,
            speakableGroupName: nil,
            conversationIdentifier: senderKey,
            serviceName: "Rosetta",
            sender: sender,
            attachments: nil
        )

        // Donate the intent so Siri can learn communication patterns.
        let interaction = INInteraction(intent: intent, response: nil)
        interaction.direction = .incoming
        interaction.donate(completion: nil)

        // Update the notification content with the intent.
        // This returns a new content object that iOS recognizes as a Communication Notification.
        do {
            let updatedContent = try content.updating(from: intent)
            return updatedContent
        } catch {
            // If updating fails, return original content — notification still works,
            // just without CarPlay / Communication Notification features.
            return content
        }
    }

    // MARK: - Helpers

    /// Android parity: extract sender key from multiple possible key names.
    private static func extractSenderKey(from userInfo: [AnyHashable: Any]) -> String {
        firstNonBlank(userInfo, keys: senderKeyNames) ?? ""
    }

    private static func firstNonBlank(_ dict: [AnyHashable: Any], keys: [String]) -> String? {
        for key in keys {
            if let value = dict[key] as? String, !value.trimmingCharacters(in: .whitespaces).isEmpty {
                return value
            }
        }
        return nil
    }
}
