package com.rosetta.messenger.network.connection

import com.rosetta.messenger.network.*

class BootstrapCoordinator(
    private val readyPacketGate: ReadyPacketGate,
    private val addLog: (String) -> Unit,
    private val shortKeyForLog: (String) -> String,
    private val sendPacketDirect: (Packet) -> Unit
) {
    fun protocolToLifecycleState(state: ProtocolState): ConnectionLifecycleState =
        when (state) {
            ProtocolState.DISCONNECTED -> ConnectionLifecycleState.DISCONNECTED
            ProtocolState.CONNECTING -> ConnectionLifecycleState.CONNECTING
            ProtocolState.CONNECTED, ProtocolState.HANDSHAKING -> ConnectionLifecycleState.HANDSHAKING
            ProtocolState.DEVICE_VERIFICATION_REQUIRED ->
                ConnectionLifecycleState.DEVICE_VERIFICATION_REQUIRED
            ProtocolState.AUTHENTICATED -> ConnectionLifecycleState.AUTHENTICATED
        }

    fun packetCanBypassReadyGate(packet: Packet): Boolean =
        when (packet) {
            is PacketHandshake,
            is PacketSync,
            is PacketSearch,
            is PacketPushNotification,
            is PacketRequestTransport,
            is PacketRequestUpdate,
            is PacketSignalPeer,
            is PacketWebRTC,
            is PacketIceServers,
            is PacketDeviceResolve -> true
            else -> false
        }

    fun recomputeLifecycleState(
        context: ConnectionBootstrapContext,
        currentState: ConnectionLifecycleState,
        reason: String,
        onStateChanged: (ConnectionLifecycleState, String) -> Unit
    ): ConnectionLifecycleState {
        val nextState =
            if (context.authenticated) {
                if (context.accountInitialized && context.syncCompleted && context.ownProfileResolved) {
                    ConnectionLifecycleState.READY
                } else {
                    ConnectionLifecycleState.BOOTSTRAPPING
                }
            } else {
                protocolToLifecycleState(context.protocolState)
            }

        if (currentState != nextState) {
            onStateChanged(nextState, reason)
        }

        if (nextState == ConnectionLifecycleState.READY) {
            flushReadyPacketQueue(context.accountPublicKey, reason)
        }

        return nextState
    }

    fun clearReadyPacketQueue(reason: String) {
        readyPacketGate.clear(reason = reason, addLog = addLog)
    }

    fun enqueueReadyPacket(
        packet: Packet,
        accountPublicKey: String,
        state: ConnectionLifecycleState
    ) {
        readyPacketGate.enqueue(
            packet = packet,
            accountPublicKey = accountPublicKey,
            state = state,
            shortKeyForLog = shortKeyForLog,
            addLog = addLog
        )
    }

    fun flushReadyPacketQueue(activeAccountKey: String, reason: String) {
        val packetsToSend =
            readyPacketGate.drainForAccount(
                activeAccountKey = activeAccountKey,
                reason = reason,
                addLog = addLog
            )
        if (packetsToSend.isEmpty()) return
        packetsToSend.forEach(sendPacketDirect)
    }
}
