package com.rosetta.messenger.network.connection

import com.rosetta.messenger.network.Packet
import com.rosetta.messenger.network.PacketIceServers
import com.rosetta.messenger.network.PacketSignalPeer
import com.rosetta.messenger.network.PacketWebRTC
import com.rosetta.messenger.network.SignalType
import com.rosetta.messenger.network.WebRTCSignalType

class CallSignalBridge(
    private val sendPacket: (Packet) -> Unit,
    private val waitPacket: (packetId: Int, callback: (Packet) -> Unit) -> Unit,
    private val unwaitPacket: (packetId: Int, callback: (Packet) -> Unit) -> Unit,
    private val addLog: (String) -> Unit,
    private val shortKeyForLog: (String, Int) -> String,
    private val shortTextForLog: (String, Int) -> String
) {
    private companion object {
        const val PACKET_SIGNAL_PEER = 0x1A
        const val PACKET_WEB_RTC = 0x1B
        const val PACKET_ICE_SERVERS = 0x1C
    }

    fun sendCallSignal(
        signalType: SignalType,
        src: String = "",
        dst: String = "",
        sharedPublic: String = "",
        callId: String = "",
        joinToken: String = ""
    ) {
        addLog(
            "📡 CALL TX type=$signalType src=${shortKeyForLog(src, 8)} dst=${shortKeyForLog(dst, 8)} " +
                "sharedLen=${sharedPublic.length} callId=${shortKeyForLog(callId, 12)} join=${shortKeyForLog(joinToken, 12)}"
        )
        sendPacket(
            PacketSignalPeer().apply {
                this.signalType = signalType
                this.src = src
                this.dst = dst
                this.sharedPublic = sharedPublic
                this.callId = callId
                this.joinToken = joinToken
            }
        )
    }

    fun sendWebRtcSignal(signalType: WebRTCSignalType, sdpOrCandidate: String) {
        addLog(
            "📡 WEBRTC TX type=$signalType sdpLen=${sdpOrCandidate.length} " +
                "preview='${shortTextForLog(sdpOrCandidate, 56)}'"
        )
        sendPacket(
            PacketWebRTC().apply {
                this.signalType = signalType
                this.sdpOrCandidate = sdpOrCandidate
            }
        )
    }

    fun requestIceServers() {
        addLog("📡 ICE TX request")
        sendPacket(PacketIceServers())
    }

    fun waitCallSignal(callback: (PacketSignalPeer) -> Unit): (Packet) -> Unit {
        val wrapper: (Packet) -> Unit = { packet ->
            (packet as? PacketSignalPeer)?.let {
                addLog(
                    "📡 CALL RX type=${it.signalType} src=${shortKeyForLog(it.src, 8)} dst=${shortKeyForLog(it.dst, 8)} " +
                        "sharedLen=${it.sharedPublic.length} callId=${shortKeyForLog(it.callId, 12)} join=${shortKeyForLog(it.joinToken, 12)}"
                )
                callback(it)
            }
        }
        waitPacket(PACKET_SIGNAL_PEER, wrapper)
        return wrapper
    }

    fun unwaitCallSignal(callback: (Packet) -> Unit) {
        unwaitPacket(PACKET_SIGNAL_PEER, callback)
    }

    fun waitWebRtcSignal(callback: (PacketWebRTC) -> Unit): (Packet) -> Unit {
        val wrapper: (Packet) -> Unit = { packet ->
            (packet as? PacketWebRTC)?.let {
                addLog(
                    "📡 WEBRTC RX type=${it.signalType} sdpLen=${it.sdpOrCandidate.length} " +
                        "preview='${shortTextForLog(it.sdpOrCandidate, 56)}'"
                )
                callback(it)
            }
        }
        waitPacket(PACKET_WEB_RTC, wrapper)
        return wrapper
    }

    fun unwaitWebRtcSignal(callback: (Packet) -> Unit) {
        unwaitPacket(PACKET_WEB_RTC, callback)
    }

    fun waitIceServers(callback: (PacketIceServers) -> Unit): (Packet) -> Unit {
        val wrapper: (Packet) -> Unit = { packet ->
            (packet as? PacketIceServers)?.let {
                val firstUrl = it.iceServers.firstOrNull()?.url.orEmpty()
                addLog("📡 ICE RX count=${it.iceServers.size} first='${shortTextForLog(firstUrl, 56)}'")
                callback(it)
            }
        }
        waitPacket(PACKET_ICE_SERVERS, wrapper)
        return wrapper
    }

    fun unwaitIceServers(callback: (Packet) -> Unit) {
        unwaitPacket(PACKET_ICE_SERVERS, callback)
    }
}
