package com.rosetta.messenger.network.connection

import com.rosetta.messenger.network.ConnectionBootstrapContext
import com.rosetta.messenger.network.ProtocolState
import java.util.Locale

interface ProtocolLifecycleStateStore {
    var bootstrapContext: ConnectionBootstrapContext

    fun clearLastSubscribedToken()
    fun cancelOwnProfileFallbackTimeout()
    fun scheduleOwnProfileFallbackTimeout(sessionGeneration: Long)
    fun nextSessionGeneration(): Long
    fun currentSessionGeneration(): Long
}

class ProtocolLifecycleCoordinator(
    private val stateStore: ProtocolLifecycleStateStore,
    private val syncCoordinator: SyncCoordinator,
    private val authBootstrapCoordinator: AuthBootstrapCoordinator,
    private val addLog: (String) -> Unit,
    private val shortKeyForLog: (String) -> String,
    private val stopWaitingForNetwork: (String) -> Unit,
    private val cancelAllOutgoingRetries: () -> Unit,
    private val recomputeConnectionLifecycleState: (String) -> Unit,
    private val onAuthenticated: () -> Unit,
    private val onSyncCompletedSideEffects: () -> Unit,
    private val updateOwnProfileResolved: (publicKey: String, reason: String) -> Unit,
) {
    fun handleProtocolStateChanged(newProtocolState: ProtocolState) {
        val context = stateStore.bootstrapContext
        val previousProtocolState = context.protocolState

        if (
            newProtocolState == ProtocolState.AUTHENTICATED &&
                previousProtocolState != ProtocolState.AUTHENTICATED
        ) {
            stateStore.clearLastSubscribedToken()
            stopWaitingForNetwork("authenticated")
            stateStore.cancelOwnProfileFallbackTimeout()
            val generation = stateStore.nextSessionGeneration()
            authBootstrapCoordinator.onAuthenticatedSessionStarted()
            stateStore.bootstrapContext =
                context.copy(
                    protocolState = newProtocolState,
                    authenticated = true,
                    syncCompleted = false,
                    ownProfileResolved = false
                )
            recomputeConnectionLifecycleState("protocol_authenticated")
            stateStore.scheduleOwnProfileFallbackTimeout(generation)
            onAuthenticated()
            return
        }

        if (
            newProtocolState != ProtocolState.AUTHENTICATED &&
                newProtocolState != ProtocolState.HANDSHAKING
        ) {
            syncCoordinator.clearRequestState()
            syncCoordinator.markSyncInProgress(false)
            stateStore.clearLastSubscribedToken()
            cancelAllOutgoingRetries()
            stateStore.cancelOwnProfileFallbackTimeout()
            authBootstrapCoordinator.reset()
            stateStore.bootstrapContext =
                context.copy(
                    protocolState = newProtocolState,
                    authenticated = false,
                    syncCompleted = false,
                    ownProfileResolved = false
                )
            recomputeConnectionLifecycleState(
                "protocol_state_${newProtocolState.name.lowercase(Locale.ROOT)}"
            )
            return
        }

        if (newProtocolState == ProtocolState.HANDSHAKING && context.authenticated) {
            stateStore.cancelOwnProfileFallbackTimeout()
            authBootstrapCoordinator.reset()
            stateStore.bootstrapContext =
                context.copy(
                    protocolState = newProtocolState,
                    authenticated = false,
                    syncCompleted = false,
                    ownProfileResolved = false
                )
            recomputeConnectionLifecycleState("protocol_re_handshaking")
            return
        }

        stateStore.bootstrapContext = context.copy(protocolState = newProtocolState)
        recomputeConnectionLifecycleState(
            "protocol_state_${newProtocolState.name.lowercase(Locale.ROOT)}"
        )
    }

    fun handleSyncCompleted(reason: String) {
        syncCoordinator.onSyncCompletedStateApplied()
        addLog(reason)
        onSyncCompletedSideEffects()

        stateStore.bootstrapContext = stateStore.bootstrapContext.copy(syncCompleted = true)
        recomputeConnectionLifecycleState("sync_completed")
    }

    fun handleOwnProfileResolved(publicKey: String) {
        val accountPublicKey = stateStore.bootstrapContext.accountPublicKey
        val matchesAccount =
            accountPublicKey.isBlank() || publicKey.equals(accountPublicKey, ignoreCase = true)
        if (!matchesAccount) return

        stateStore.cancelOwnProfileFallbackTimeout()
        stateStore.bootstrapContext = stateStore.bootstrapContext.copy(ownProfileResolved = true)
        updateOwnProfileResolved(publicKey, "protocol_own_profile_resolved")
        recomputeConnectionLifecycleState("own_profile_resolved")
    }

    fun handleOwnProfileFallbackTimeout(sessionGeneration: Long) {
        if (stateStore.currentSessionGeneration() != sessionGeneration) return

        val context = stateStore.bootstrapContext
        if (!context.authenticated || context.ownProfileResolved) return

        addLog(
            "⏱️ Own profile fetch timeout — continuing bootstrap for ${shortKeyForLog(context.accountPublicKey)}"
        )
        val updatedContext = context.copy(ownProfileResolved = true)
        stateStore.bootstrapContext = updatedContext

        val accountPublicKey = updatedContext.accountPublicKey
        if (accountPublicKey.isNotBlank()) {
            updateOwnProfileResolved(accountPublicKey, "protocol_own_profile_fallback_timeout")
        }
        recomputeConnectionLifecycleState("own_profile_fallback_timeout")
    }
}
