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

import Foundation
import LibSignalClient

final class RecipientStateMerger {
    private let recipientDatabaseTable: RecipientDatabaseTable
    private let signalServiceAddressCache: SignalServiceAddressCache

    init(
        recipientDatabaseTable: RecipientDatabaseTable,
        signalServiceAddressCache: SignalServiceAddressCache
    ) {
        self.recipientDatabaseTable = recipientDatabaseTable
        self.signalServiceAddressCache = signalServiceAddressCache
    }

    func normalize(_ recipientStates: inout [SignalServiceAddress: TSOutgoingMessageRecipientState]?, tx: DBReadTransaction) {
        guard let oldRecipientStates = recipientStates else {
            return
        }
        var existingValues = [(SignalServiceAddress, TSOutgoingMessageRecipientState)]()
        // If we convert a Pni to an Aci, it's possible the Aci is already in
        // recipientStates. If that's the case, we want to throw away the Pni and
        // defer to the Aci. We do this by handling Pnis after everything else.
        var updatedValues = [(SignalServiceAddress, TSOutgoingMessageRecipientState)]()
        for (oldAddress, recipientState) in oldRecipientStates {
            if let normalizedAddress = normalizedAddressIfNeeded(for: oldAddress, tx: tx) {
                updatedValues.append((normalizedAddress, recipientState))
            } else {
                existingValues.append((oldAddress, recipientState))
            }
        }
        recipientStates = Dictionary(existingValues + updatedValues, uniquingKeysWith: { lhs, _ in lhs })
    }

    func normalizedAddressIfNeeded(for oldAddress: SignalServiceAddress, tx: DBReadTransaction) -> SignalServiceAddress? {
        switch oldAddress.serviceId?.concreteType {
        case .none, .aci:
            return nil
        case .pni(let pni):
            guard let aci = recipientDatabaseTable.fetchRecipient(serviceId: pni, transaction: tx)?.aci else {
                return nil
            }
            return SignalServiceAddress(
                serviceId: aci,
                phoneNumber: nil,
                cache: signalServiceAddressCache
            )
        }
    }
}
