//
//  Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//

import Foundation
import PromiseKit
import SignalServiceKit
import SignalMessaging

/**
 * Used to distribute our profile key to legacy linked devices, newly linked devices will have our profile key as part of provisioning.
 * Syncing is accomplished via the existing contact syncing mechanism, except the only contact synced is ourself. It's incumbent on the linked device
 * to treat this "self contact" record specially.
 */
@objc public class MultiDeviceProfileKeyUpdateJob: NSObject {

    let TAG = "[MultiDeviceProfileKeyUpdateJob]"

    private let profileKey: OWSAES256Key
    private let identityManager: OWSIdentityManager
    private let messageSender: MessageSender
    private let profileManager: OWSProfileManager
    private var editingDatabaseConnection: YapDatabaseConnection {
        return OWSPrimaryStorage.shared().dbReadWriteConnection
    }

   @objc public required init(profileKey: OWSAES256Key, identityManager: OWSIdentityManager, messageSender: MessageSender, profileManager: OWSProfileManager) {
        self.profileKey = profileKey

        self.identityManager = identityManager
        self.messageSender = messageSender
        self.profileManager = profileManager
    }

    @objc public class func run(profileKey: OWSAES256Key, identityManager: OWSIdentityManager, messageSender: MessageSender, profileManager: OWSProfileManager) {
        return self.init(profileKey: profileKey, identityManager: identityManager, messageSender: messageSender, profileManager: profileManager).run()
    }

    func run(retryDelay: TimeInterval = 1) {
        guard let localNumber = TSAccountManager.localNumber() else {
            owsFail("\(self.TAG) localNumber was unexpectedly nil")
            return
        }

        let localSignalAccount = SignalAccount(recipientId: localNumber)
        localSignalAccount.contact = Contact()
        let syncContactsMessage = OWSSyncContactsMessage(signalAccounts: [localSignalAccount],
                                                        identityManager: self.identityManager,
                                                        profileManager: self.profileManager)

        var dataSource: DataSource?
        self.editingDatabaseConnection.readWrite { transaction in
            guard let messageData: Data = syncContactsMessage.buildPlainTextAttachmentData(with: transaction) else {
                owsFail("\(self.logTag) could not serialize sync contacts data")
                return
            }
            dataSource = DataSourceValue.dataSource(withSyncMessageData: messageData)
        }

        guard let attachmentDataSource = dataSource else {
            owsFail("\(self.logTag) in \(#function) dataSource was unexpectedly nil")
            return
        }

        self.messageSender.enqueueTemporaryAttachment(attachmentDataSource,
            contentType: OWSMimeTypeApplicationOctetStream,
            in: syncContactsMessage,
            success: {
                Logger.info("\(self.TAG) Successfully synced profile key")
            },
            failure: { error in
                Logger.error("\(self.TAG) in \(#function) failed with error: \(error) retrying in \(retryDelay)s.")
                after(interval: retryDelay).then {
                    self.run(retryDelay: retryDelay * 2)
                }.retainUntilComplete()
            })
    }
}
