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

import Foundation
import LibSignalClient
import SignalServiceKit

class WebRTCCallMessageHandler: CallMessageHandler {

    // MARK: Initializers

    public init() {
        SwiftSingletons.register(self)
    }

    private var callService: CallService { AppEnvironment.shared.callService }
    private var groupCallManager: GroupCallManager { NSObject.groupCallManager }
    private var tsAccountManager: TSAccountManager { DependenciesBridge.shared.tsAccountManager }

    // MARK: - Call Handlers

    func receivedEnvelope(
        _ envelope: SSKProtoEnvelope,
        callEnvelope: CallEnvelopeType,
        from caller: (aci: Aci, deviceId: UInt32),
        plaintextData: Data,
        wasReceivedByUD: Bool,
        sentAtTimestamp: UInt64,
        serverReceivedTimestamp: UInt64,
        serverDeliveryTimestamp: UInt64,
        tx: SDSAnyWriteTransaction
    ) {
        switch callEnvelope {
        case .offer(let offer):
            self.callService.individualCallService.handleReceivedOffer(
                caller: caller.aci,
                callId: offer.id,
                sourceDevice: caller.deviceId,
                opaque: offer.opaque,
                sentAtTimestamp: sentAtTimestamp,
                serverReceivedTimestamp: serverReceivedTimestamp,
                serverDeliveryTimestamp: serverDeliveryTimestamp,
                callType: offer.type ?? .offerAudioCall,
                tx: tx
            )
        case .answer(let answer):
            self.callService.individualCallService.handleReceivedAnswer(
                caller: caller.aci,
                callId: answer.id,
                sourceDevice: caller.deviceId,
                opaque: answer.opaque,
                tx: tx
            )
        case .iceUpdate(let iceUpdate):
            self.callService.individualCallService.handleReceivedIceCandidates(
                caller: caller.aci,
                callId: iceUpdate[0].id,
                sourceDevice: caller.deviceId,
                candidates: iceUpdate
            )
        case .hangup(let hangup):
            // deviceId is optional and defaults to 0.
            var deviceId: UInt32 = 0

            let type: SSKProtoCallMessageHangupType
            if hangup.hasType {
                type = hangup.unwrappedType

                if hangup.hasDeviceID {
                    deviceId = hangup.deviceID
                }
            } else {
                // The type is not defined so assume the default, normal.
                type = .hangupNormal
            }

            self.callService.individualCallService.handleReceivedHangup(
                caller: caller.aci,
                callId: hangup.id,
                sourceDevice: caller.deviceId,
                type: type,
                deviceId: deviceId
            )
        case .busy(let busy):
            self.callService.individualCallService.handleReceivedBusy(
                caller: caller.aci,
                callId: busy.id,
                sourceDevice: caller.deviceId
            )
        case .opaque(let opaque):
            Logger.info("Received opaque call message from \(caller.aci).\(caller.deviceId)")

            guard let message = opaque.data else {
                owsFailDebug("Received opaque call message without data")
                return
            }

            var messageAgeSec: UInt64 = 0
            if serverReceivedTimestamp > 0 && serverDeliveryTimestamp >= serverReceivedTimestamp {
                messageAgeSec = (serverDeliveryTimestamp - serverReceivedTimestamp) / 1000
            }

            let localDeviceId = tsAccountManager.storedDeviceId(tx: tx.asV2Read)

            DispatchQueue.main.async {
                self.callService.callManager.receivedCallMessage(
                    senderUuid: caller.aci.rawUUID,
                    senderDeviceId: caller.deviceId,
                    localDeviceId: localDeviceId,
                    message: message,
                    messageAgeSec: messageAgeSec
                )
            }
        }
    }

    func receivedGroupCallUpdateMessage(
        _ updateMessage: SSKProtoDataMessageGroupCallUpdate,
        for groupThread: TSGroupThread,
        serverReceivedTimestamp: UInt64
    ) async {
        await groupCallManager.peekGroupCallAndUpdateThread(
            groupThread,
            peekTrigger: .receivedGroupUpdateMessage(
                eraId: updateMessage.eraID,
                messageTimestamp: serverReceivedTimestamp
            )
        )
    }
}
