//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

public import LibSignalClient

public protocol NotificationPresenter {
    func registerNotificationSettings() async

    func notifyUser(forIncomingMessage: TSIncomingMessage, thread: TSThread, transaction: DBWriteTransaction)

    func notifyUser(forIncomingMessage: TSIncomingMessage, editTarget: TSIncomingMessage, thread: TSThread, transaction: DBWriteTransaction)

    func notifyUser(forReaction: OWSReaction, onOutgoingMessage: TSOutgoingMessage, thread: TSThread, transaction: DBWriteTransaction)

    func notifyUser(forErrorMessage: TSErrorMessage, thread: TSThread, transaction: DBWriteTransaction)

    func notifyUser(forTSMessage: TSMessage, thread: TSThread, wantsSound: Bool, transaction: DBWriteTransaction)

    func notifyUserOfPollEnd(forMessage message: TSIncomingMessage, thread: TSThread, transaction: DBWriteTransaction)

    func notifyUserOfPollVote(forMessage message: TSOutgoingMessage, voteAuthor: Aci, thread: TSThread, transaction: DBWriteTransaction)

    func notifyUser(forPreviewableInteraction: TSInteraction & OWSPreviewText, thread: TSThread, wantsSound: Bool, transaction: DBWriteTransaction)

    func notifyTestPopulation(ofErrorMessage errorString: String)

    func notifyUser(forFailedStorySend: StoryMessage, to: TSThread, transaction: DBWriteTransaction)

    func notifyUserOfFailedSend(inThread thread: TSThread)

    func notifyUserOfMissedCall(
        notificationInfo: CallNotificationInfo,
        offerMediaType: TSRecentCallOfferType,
        sentAt timestamp: Date,
        tx: DBReadTransaction,
    )

    func notifyUserOfMissedCallBecauseOfNewIdentity(
        notificationInfo: CallNotificationInfo,
        tx: DBWriteTransaction,
    )

    func notifyUserOfMissedCallBecauseOfNoLongerVerifiedIdentity(
        notificationInfo: CallNotificationInfo,
        tx: DBWriteTransaction,
    )

    func notifyForGroupCallSafetyNumberChange(
        callTitle: String,
        threadUniqueId: String?,
        roomId: Data?,
        presentAtJoin: Bool,
    )

    func scheduleNotifyForNewLinkedDevice(deviceLinkTimestamp: Date)

    func scheduleNotifyForBackupsEnabled(backupsTimestamp: Date)

    func notifyUserOfAttachmentBackfill(
        threadUniqueId: String,
        messageUniqueId: String,
        body: String,
    )

    func notifyUserOfMediaTierQuotaConsumed()

    func notifyUserOfBackupsMediaError()

    /// Notify user to relaunch the app after we deliberately terminate when an incoming device transfer completes.
    func notifyUserToRelaunchAfterTransfer(completion: @escaping () -> Void)

    /// Notify user of an auth error that has caused their device to be logged out (e.g. a 403 from the chat server).
    func notifyUserOfDeregistration(tx: DBWriteTransaction)

    func clearAllNotifications()
    func clearNotificationsForAppActivate()
    func clearDeliveredNewLinkedDevicesNotifications()

    func cancelNotifications(threadId: String)

    func cancelNotifications(messageIds: [String])

    func cancelNotifications(reactionId: String)

    func cancelNotificationsForMissedCalls(threadUniqueId: String)

    func cancelNotifications(for storyMessage: StoryMessage)
}

@objc
class NotificationPresenterObjC: NSObject {
    @objc(cancelNotificationsForMessageId:)
    static func cancelNotifications(for messageId: String) {
        SSKEnvironment.shared.notificationPresenterRef.cancelNotifications(messageIds: [messageId])
    }
}

// MARK: -

public struct CallNotificationInfo {
    /// Basically a per-call unique identifier. When posting multiple
    /// notifications with the same `groupingId`, only the latest notification
    /// will be shown.
    let groupingId: UUID

    /// The thread that was called.
    let thread: TSContactThread

    /// The user who called the thread.
    let caller: Aci

    public init(groupingId: UUID, thread: TSContactThread, caller: Aci) {
        self.groupingId = groupingId
        self.thread = thread
        self.caller = caller
    }
}

// MARK: -

/// Which notifications should be suppressed (based on which view is currently visible).
enum NotificationSuppressionRule {
    /// Includes reactions to messages in the thread.
    case messagesInThread(threadUniqueId: String)
    case groupStoryReplies(threadUniqueId: String?, storyMessageTimestamp: UInt64)
    case failedStorySends
    /// Suppress all notifications
    case all
    /// Don't suppress any notifications
    case none
}
