package com.rosetta.messenger.network

import android.content.Context
import com.rosetta.messenger.data.AccountManager
import com.rosetta.messenger.data.GroupRepository
import com.rosetta.messenger.data.MessageRepository
import com.rosetta.messenger.di.ProtocolGateway
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class ProtocolRuntime @Inject constructor(
    private val runtimeComposition: RuntimeComposition,
    private val messageRepository: MessageRepository,
    private val groupRepository: GroupRepository,
    private val accountManager: AccountManager
) : ProtocolRuntimePort, ProtocolGateway {
    init {
        bindDependencies()
    }

    private val connectionControlApi by lazy { runtimeComposition.connectionControlApi() }
    private val directoryApi by lazy { runtimeComposition.directoryApi() }
    private val packetIoApi by lazy { runtimeComposition.packetIoApi() }

    override val state: StateFlow<ProtocolState> get() = runtimeComposition.state
    override val syncInProgress: StateFlow<Boolean> get() = runtimeComposition.syncInProgress
    override val pendingDeviceVerification: StateFlow<DeviceEntry?> get() =
        runtimeComposition.pendingDeviceVerification
    override val typingUsers: StateFlow<Set<String>> get() = runtimeComposition.typingUsers
    override val typingUsersByDialogSnapshot: StateFlow<Map<String, Set<String>>> get() =
        runtimeComposition.typingUsersByDialogSnapshot
    override val debugLogs: StateFlow<List<String>> get() = runtimeComposition.debugLogs
    override val ownProfileUpdated: StateFlow<Long> get() = runtimeComposition.ownProfileUpdated

    override fun initialize(context: Context) {
        bindDependencies()
        connectionControlApi.initialize(context)
    }

    override fun initializeAccount(publicKey: String, privateKey: String) =
        connectionControlApi.initializeAccount(publicKey, privateKey)

    override fun connect() = connectionControlApi.connect()

    override fun authenticate(publicKey: String, privateHash: String) =
        connectionControlApi.authenticate(publicKey, privateHash)

    override fun reconnectNowIfNeeded(reason: String) =
        connectionControlApi.reconnectNowIfNeeded(reason)

    override fun disconnect() = connectionControlApi.disconnect()

    override fun setAppInForeground(foreground: Boolean) =
        connectionControlApi.setAppInForeground(foreground)

    override fun isAuthenticated(): Boolean = connectionControlApi.isAuthenticated()

    override fun getPrivateHash(): String? = connectionControlApi.getPrivateHashOrNull()

    override fun subscribePushTokenIfAvailable(forceToken: String?) =
        connectionControlApi.subscribePushToken(forceToken)

    override fun addLog(message: String) = runtimeComposition.addLog(message)

    override fun enableUILogs(enabled: Boolean) = runtimeComposition.enableUILogs(enabled)

    override fun clearLogs() = runtimeComposition.clearLogs()

    override fun resolveOutgoingRetry(messageId: String) =
        packetIoApi.resolveOutgoingRetry(messageId)

    override fun getCachedUserByUsername(username: String): SearchUser? =
        directoryApi.getCachedUserByUsername(username)

    override fun getCachedUserName(publicKey: String): String? =
        directoryApi.getCachedUserName(publicKey)

    override fun getCachedUserInfo(publicKey: String): SearchUser? =
        directoryApi.getCachedUserInfo(publicKey)

    override fun acceptDevice(deviceId: String) = directoryApi.acceptDevice(deviceId)

    override fun declineDevice(deviceId: String) = directoryApi.declineDevice(deviceId)

    override fun send(packet: Packet) = packetIoApi.send(packet)

    override fun sendPacket(packet: Packet) = packetIoApi.sendPacket(packet)

    override fun sendMessageWithRetry(packet: PacketMessage) =
        packetIoApi.sendMessageWithRetry(packet)

    override fun waitPacket(packetId: Int, callback: (Packet) -> Unit) =
        packetIoApi.waitPacket(packetId, callback)

    override fun unwaitPacket(packetId: Int, callback: (Packet) -> Unit) =
        packetIoApi.unwaitPacket(packetId, callback)

    override fun packetFlow(packetId: Int): SharedFlow<Packet> =
        packetIoApi.packetFlow(packetId)

    override fun notifyOwnProfileUpdated() = directoryApi.notifyOwnProfileUpdated()

    override fun restoreAuthFromStoredCredentials(
        preferredPublicKey: String?,
        reason: String
    ): Boolean = connectionControlApi.restoreAuthFromStoredCredentials(preferredPublicKey, reason)

    override suspend fun resolveUserName(publicKey: String, timeoutMs: Long): String? =
        directoryApi.resolveUserName(publicKey, timeoutMs)

    override suspend fun resolveUserInfo(publicKey: String, timeoutMs: Long): SearchUser? =
        directoryApi.resolveUserInfo(publicKey, timeoutMs)

    override suspend fun searchUsers(query: String, timeoutMs: Long): List<SearchUser> =
        directoryApi.searchUsers(query, timeoutMs)

    override fun requestIceServers() = packetIoApi.requestIceServers()

    override fun waitCallSignal(callback: (PacketSignalPeer) -> Unit): (Packet) -> Unit =
        packetIoApi.waitCallSignal(callback)

    override fun unwaitCallSignal(callback: (Packet) -> Unit) =
        packetIoApi.unwaitCallSignal(callback)

    override fun waitWebRtcSignal(callback: (PacketWebRTC) -> Unit): (Packet) -> Unit =
        packetIoApi.waitWebRtcSignal(callback)

    override fun unwaitWebRtcSignal(callback: (Packet) -> Unit) =
        packetIoApi.unwaitWebRtcSignal(callback)

    override fun waitIceServers(callback: (PacketIceServers) -> Unit): (Packet) -> Unit =
        packetIoApi.waitIceServers(callback)

    override fun unwaitIceServers(callback: (Packet) -> Unit) =
        packetIoApi.unwaitIceServers(callback)

    override fun sendCallSignal(
        signalType: SignalType,
        src: String,
        dst: String,
        sharedPublic: String,
        callId: String,
        joinToken: String
    ) = packetIoApi.sendCallSignal(signalType, src, dst, sharedPublic, callId, joinToken)

    override fun sendWebRtcSignal(signalType: WebRTCSignalType, sdpOrCandidate: String) =
        packetIoApi.sendWebRtcSignal(signalType, sdpOrCandidate)

    private fun bindDependencies() {
        runtimeComposition.bindDependencies(
            messageRepository = messageRepository,
            groupRepository = groupRepository,
            accountManager = accountManager
        )
    }
}
