import Testing
import UserNotifications
@testable import Rosetta

// MARK: - Foreground Notification Suppression Tests

/// Tests for foreground notification suppression logic.
/// `willPresent` returns `[.banner, .sound]` by default, `[]` for active/muted chats.
@MainActor
struct ForegroundNotificationTests {

    private func clearActiveDialogs() {
        for key in MessageRepository.shared.activeDialogKeys {
            MessageRepository.shared.setDialogActive(key, isActive: false)
        }
    }

    // MARK: - System Banner Presentation

    @Test("Telegram parity: all foreground notifications suppressed")
    func nonSuppressedShowsBanner() {
        clearActiveDialogs()
        let userInfo: [AnyHashable: Any] = ["dialog": "02aaa", "title": "Alice"]
        let options = AppDelegate.foregroundPresentationOptions(for: userInfo)
        // Telegram parity: willPresent returns [] — no banners/sound in foreground.
        #expect(options == [])
    }

    @Test("Active chat suppresses system banner")
    func activeChatSuppressesBanner() {
        clearActiveDialogs()
        MessageRepository.shared.setDialogActive("02bbb", isActive: true)
        let userInfo: [AnyHashable: Any] = ["dialog": "02bbb"]
        #expect(AppDelegate.foregroundPresentationOptions(for: userInfo) == [])
        MessageRepository.shared.setDialogActive("02bbb", isActive: false)
    }

    // MARK: - In-App Banner: Active Chat → Suppress

    @Test("Active chat is suppressed by shouldSuppress")
    func activeChatSuppressed() {
        clearActiveDialogs()
        MessageRepository.shared.setDialogActive("02aaa", isActive: true)

        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02aaa") == true)

        MessageRepository.shared.setDialogActive("02aaa", isActive: false)
    }

    @Test("Inactive chat is NOT suppressed")
    func inactiveChatNotSuppressed() {
        clearActiveDialogs()
        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02bbb") == false)
    }

    @Test("Only active chat suppressed, other chats shown")
    func onlyActiveSuppressed() {
        clearActiveDialogs()
        MessageRepository.shared.setDialogActive("02aaa", isActive: true)

        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02aaa") == true)
        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02bbb") == false)

        MessageRepository.shared.setDialogActive("02aaa", isActive: false)
    }

    // MARK: - Dialog Deactivation

    @Test("After closing chat, notifications from it are no longer suppressed")
    func deactivatedChatNotSuppressed() {
        clearActiveDialogs()
        MessageRepository.shared.setDialogActive("02aaa", isActive: true)
        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02aaa") == true)

        MessageRepository.shared.setDialogActive("02aaa", isActive: false)
        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02aaa") == false)
    }

    // MARK: - Empty / Missing Sender Key

    @Test("Empty sender key is suppressed (can't navigate to empty chat)")
    func emptySenderKeySuppressed() {
        clearActiveDialogs()
        #expect(InAppNotificationManager.shouldSuppress(senderKey: "") == true)
    }

    // MARK: - Multiple Active Dialogs

    @Test("Multiple active dialogs — each independently suppressed")
    func multipleActiveDialogs() {
        clearActiveDialogs()
        MessageRepository.shared.setDialogActive("02aaa", isActive: true)
        MessageRepository.shared.setDialogActive("02bbb", isActive: true)

        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02aaa") == true)
        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02bbb") == true)
        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02ccc") == false)

        MessageRepository.shared.setDialogActive("02aaa", isActive: false)
        MessageRepository.shared.setDialogActive("02bbb", isActive: false)
    }

    // MARK: - Muted Chat Suppression

    @Test("Muted chat is suppressed")
    func mutedChatSuppressed() {
        clearActiveDialogs()
        // Production checks DialogRepository (in-memory), not UserDefaults.
        DialogRepository.shared.ensureDialog(
            opponentKey: "02muted", title: "", username: "", myPublicKey: "me"
        )
        DialogRepository.shared.toggleMute(opponentKey: "02muted")

        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02muted") == true)

        // Restore — toggleMute back to unmuted, then remove via ensureDialog pattern isn't needed
        // since dialogs dict is in-memory and won't persist in test.
        DialogRepository.shared.toggleMute(opponentKey: "02muted")
    }

    @Test("Non-muted chat is NOT suppressed")
    func nonMutedChatNotSuppressed() {
        clearActiveDialogs()
        let shared = UserDefaults(suiteName: "group.com.rosetta.dev")
        let originalMuted = shared?.stringArray(forKey: "muted_chats_keys")
        shared?.set(["02other"], forKey: "muted_chats_keys")

        #expect(InAppNotificationManager.shouldSuppress(senderKey: "02notmuted") == false)

        shared?.set(originalMuted, forKey: "muted_chats_keys")
    }

    // MARK: - Sender Key Extraction (AppDelegate)

    @Test("extractSenderKey reads 'dialog' field first")
    func extractSenderKeyDialog() {
        let userInfo: [AnyHashable: Any] = ["dialog": "02aaa", "sender_public_key": "02bbb"]
        let key = AppDelegate.extractSenderKey(from: userInfo)
        #expect(key == "02aaa")
    }

    @Test("extractSenderKey falls back to 'sender_public_key'")
    func extractSenderKeyFallback() {
        let userInfo: [AnyHashable: Any] = ["sender_public_key": "02ccc"]
        let key = AppDelegate.extractSenderKey(from: userInfo)
        #expect(key == "02ccc")
    }

    @Test("extractSenderKey falls back to 'fromPublicKey'")
    func extractSenderKeyFromPublicKey() {
        let userInfo: [AnyHashable: Any] = ["fromPublicKey": "02ddd"]
        let key = AppDelegate.extractSenderKey(from: userInfo)
        #expect(key == "02ddd")
    }

    @Test("extractSenderKey returns empty for missing keys")
    func extractSenderKeyEmpty() {
        let userInfo: [AnyHashable: Any] = ["type": "personal_message"]
        let key = AppDelegate.extractSenderKey(from: userInfo)
        #expect(key == "")
    }
}
