package com.rosetta.messenger.network

import com.rosetta.messenger.data.GroupRepository
import com.rosetta.messenger.data.MessageRepository
import com.rosetta.messenger.network.connection.CallSignalBridge
import com.rosetta.messenger.network.connection.DeviceRuntimeService
import com.rosetta.messenger.network.connection.InboundPacketHandlerRegistrar
import com.rosetta.messenger.network.connection.InboundTaskQueueService
import com.rosetta.messenger.network.connection.OutgoingMessagePipelineService
import com.rosetta.messenger.network.connection.OwnProfileSyncService
import com.rosetta.messenger.network.connection.PacketRouter
import com.rosetta.messenger.network.connection.PresenceTypingService
import com.rosetta.messenger.network.connection.SyncCoordinator
import kotlinx.coroutines.CoroutineScope

internal class RuntimeMessagingAssembly(
    tag: String,
    scope: CoroutineScope,
    typingIndicatorTimeoutMs: Long,
    syncRequestTimeoutMs: Long,
    manualSyncBacktrackMs: Long,
    deviceRuntimeService: DeviceRuntimeService,
    ownProfileSyncService: OwnProfileSyncService,
    isAuthenticated: () -> Boolean,
    getProtocolPublicKey: () -> String,
    getProtocolPrivateHash: () -> String?,
    getMessageRepository: () -> MessageRepository?,
    getGroupRepository: () -> GroupRepository?,
    sendSearchPacket: (PacketSearch) -> Unit,
    sendMessagePacket: (PacketMessage) -> Unit,
    sendSyncPacket: (PacketSync) -> Unit,
    sendPacket: (Packet) -> Unit,
    waitPacket: (Int, (Packet) -> Unit) -> Unit,
    unwaitPacket: (Int, (Packet) -> Unit) -> Unit,
    addLog: (String) -> Unit,
    shortKeyForLog: (String, Int) -> String,
    shortTextForLog: (String, Int) -> String,
    onSyncCompleted: (String) -> Unit,
    onInboundTaskQueued: () -> Unit,
    onInboundTaskFailure: (String, Throwable?) -> Unit,
    resolveOutgoingRetry: (String) -> Unit,
    isGroupDialogKey: (String) -> Boolean,
    setTransportServer: (String) -> Unit,
    onOwnProfileResolved: (String) -> Unit
) {
    val packetRouter =
        PacketRouter(
            sendSearchPacket = sendSearchPacket,
            privateHashProvider = getProtocolPrivateHash
        )

    val outgoingMessagePipelineService =
        OutgoingMessagePipelineService(
            scope = scope,
            getRepository = getMessageRepository,
            sendPacket = sendMessagePacket,
            isAuthenticated = isAuthenticated,
            addLog = addLog
        )

    val presenceTypingService =
        PresenceTypingService(
            scope = scope,
            typingIndicatorTimeoutMs = typingIndicatorTimeoutMs
        )

    val inboundTaskQueueService =
        InboundTaskQueueService(
            scope = scope,
            onTaskQueued = onInboundTaskQueued,
            onTaskFailure = onInboundTaskFailure
        )

    val syncCoordinator =
        SyncCoordinator(
            scope = scope,
            syncRequestTimeoutMs = syncRequestTimeoutMs,
            manualSyncBacktrackMs = manualSyncBacktrackMs,
            addLog = addLog,
            isAuthenticated = isAuthenticated,
            getRepository = getMessageRepository,
            getProtocolPublicKey = getProtocolPublicKey,
            sendPacket = sendSyncPacket,
            onSyncCompleted = onSyncCompleted,
            whenInboundTasksFinish = inboundTaskQueueService::whenTasksFinish
        )

    val callSignalBridge =
        CallSignalBridge(
            sendPacket = sendPacket,
            waitPacket = waitPacket,
            unwaitPacket = unwaitPacket,
            addLog = addLog,
            shortKeyForLog = shortKeyForLog,
            shortTextForLog = shortTextForLog
        )

    val inboundPacketHandlerRegistrar =
        InboundPacketHandlerRegistrar(
            tag = tag,
            scope = scope,
            syncCoordinator = syncCoordinator,
            presenceTypingService = presenceTypingService,
            deviceRuntimeService = deviceRuntimeService,
            packetRouter = packetRouter,
            ownProfileSyncService = ownProfileSyncService,
            waitPacket = waitPacket,
            launchInboundPacketTask = inboundTaskQueueService::enqueue,
            getMessageRepository = getMessageRepository,
            getGroupRepository = getGroupRepository,
            getProtocolPublicKey = { getProtocolPublicKey().trim().orEmpty() },
            addLog = addLog,
            markInboundProcessingFailure = onInboundTaskFailure,
            resolveOutgoingRetry = resolveOutgoingRetry,
            isGroupDialogKey = isGroupDialogKey,
            onOwnProfileResolved = onOwnProfileResolved,
            setTransportServer = setTransportServer
        )
}
