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

import Foundation
import LibSignalClient

class PhoneNumberChangedMessageInserter: RecipientMergeObserver {
    private let groupMemberStore: GroupMemberStore
    private let interactionStore: InteractionStore
    private let threadAssociatedDataStore: ThreadAssociatedDataStore
    private let threadStore: ThreadStore

    init(
        groupMemberStore: GroupMemberStore,
        interactionStore: InteractionStore,
        threadAssociatedDataStore: ThreadAssociatedDataStore,
        threadStore: ThreadStore
    ) {
        self.groupMemberStore = groupMemberStore
        self.interactionStore = interactionStore
        self.threadAssociatedDataStore = threadAssociatedDataStore
        self.threadStore = threadStore
    }

    func willBreakAssociation(for recipient: SignalRecipient, mightReplaceNonnilPhoneNumber: Bool, tx: DBWriteTransaction) {}

    func didLearnAssociation(mergedRecipient: MergedRecipient, tx: DBWriteTransaction) {
        guard !mergedRecipient.isLocalRecipient else {
            // Don't insert change number messages when we change our own number.
            return
        }
        guard
            let aci = mergedRecipient.newRecipient.aci,
            let oldPhoneNumber = mergedRecipient.oldRecipient?.phoneNumber?.stringValue,
            let newPhoneNumber = E164(mergedRecipient.newRecipient.phoneNumber?.stringValue),
            oldPhoneNumber != newPhoneNumber.stringValue
        else {
            // Don't insert change number messages unless we've *changed* from an old
            // number to a new number.
            return
        }

        func insertChangeMessage(thread: TSThread) {
            guard thread.shouldThreadBeVisible else {
                // Skip if thread is soft deleted or otherwise not user visible.
                return
            }
            let threadAssociatedData = threadAssociatedDataStore.fetchOrDefault(for: thread, tx: tx)
            guard !threadAssociatedData.isArchived else {
                // Skip if thread is archived.
                return
            }
            let infoMessage = TSInfoMessage(thread: thread, messageType: .phoneNumberChange, infoMessageUserInfo: [
                .changePhoneNumberAciString: aci.serviceIdUppercaseString,
                .changePhoneNumberOld: oldPhoneNumber,
                .changePhoneNumberNew: newPhoneNumber.stringValue
            ])
            infoMessage.wasRead = true
            interactionStore.insertInteraction(infoMessage, tx: tx)
        }

        // Only insert "change phone number" interactions for full members.
        for threadId in groupMemberStore.groupThreadIds(withFullMember: aci, tx: tx) {
            guard let thread = threadStore.fetchGroupThread(uniqueId: threadId, tx: tx) else {
                continue
            }
            insertChangeMessage(thread: thread)
        }

        // Only insert "change phone number" interaction in 1:1 thread if it already exists.
        if let thread = threadStore.fetchContactThread(recipient: mergedRecipient.newRecipient, tx: tx) {
            insertChangeMessage(thread: thread)
        }
    }
}
