package com.rosetta.messenger.network.connection

import android.util.Log
import com.rosetta.messenger.data.GroupRepository
import com.rosetta.messenger.data.MessageRepository
import com.rosetta.messenger.network.OnlineState
import com.rosetta.messenger.network.Packet
import com.rosetta.messenger.network.PacketDelivery
import com.rosetta.messenger.network.PacketDeviceList
import com.rosetta.messenger.network.PacketDeviceNew
import com.rosetta.messenger.network.PacketGroupJoin
import com.rosetta.messenger.network.PacketMessage
import com.rosetta.messenger.network.PacketOnlineState
import com.rosetta.messenger.network.PacketRead
import com.rosetta.messenger.network.PacketRequestTransport
import com.rosetta.messenger.network.PacketSearch
import com.rosetta.messenger.network.PacketSync
import com.rosetta.messenger.network.PacketTyping
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class InboundPacketHandlerRegistrar(
    private val tag: String,
    private val scope: CoroutineScope,
    private val syncCoordinator: SyncCoordinator,
    private val presenceTypingService: PresenceTypingService,
    private val deviceRuntimeService: DeviceRuntimeService,
    private val packetRouter: PacketRouter,
    private val ownProfileSyncService: OwnProfileSyncService,
    private val waitPacket: (Int, (Packet) -> Unit) -> Unit,
    private val launchInboundPacketTask: (suspend () -> Unit) -> Boolean,
    private val getMessageRepository: () -> MessageRepository?,
    private val getGroupRepository: () -> GroupRepository?,
    private val getProtocolPublicKey: () -> String,
    private val addLog: (String) -> Unit,
    private val markInboundProcessingFailure: (String, Throwable?) -> Unit,
    private val resolveOutgoingRetry: (String) -> Unit,
    private val isGroupDialogKey: (String) -> Boolean,
    private val onOwnProfileResolved: (String) -> Unit,
    private val setTransportServer: (String) -> Unit
) {
    fun register() {
        registerIncomingMessageHandler()
        registerDeliveryHandler()
        registerReadHandler()
        registerDeviceLoginHandler()
        registerSyncHandler()
        registerGroupSyncHandler()
        registerOnlineStatusHandler()
        registerTypingHandler()
        registerDeviceListHandler()
        registerSearchHandler()
        registerTransportHandler()
    }

    private fun registerIncomingMessageHandler() {
        waitPacket(0x06) { packet ->
            val messagePacket = packet as PacketMessage

            launchInboundPacketTask {
                val repository = getMessageRepository()
                if (repository == null || !repository.isInitialized()) {
                    syncCoordinator.requireResyncAfterAccountInit(
                        "⏳ Incoming message before account init, scheduling re-sync"
                    )
                    markInboundProcessingFailure("Incoming packet skipped before account init", null)
                    return@launchInboundPacketTask
                }
                val processed = repository.handleIncomingMessage(messagePacket)
                if (!processed) {
                    markInboundProcessingFailure(
                        "Message processing failed for ${messagePacket.messageId.take(8)}",
                        null
                    )
                    return@launchInboundPacketTask
                }
                if (!syncCoordinator.isBatchInProgress()) {
                    repository.updateLastSyncTimestamp(messagePacket.timestamp)
                }
            }
        }
    }

    private fun registerDeliveryHandler() {
        waitPacket(0x08) { packet ->
            val deliveryPacket = packet as PacketDelivery

            launchInboundPacketTask {
                val repository = getMessageRepository()
                if (repository == null || !repository.isInitialized()) {
                    syncCoordinator.requireResyncAfterAccountInit(
                        "⏳ Delivery status before account init, scheduling re-sync"
                    )
                    markInboundProcessingFailure("Delivery packet skipped before account init", null)
                    return@launchInboundPacketTask
                }
                try {
                    repository.handleDelivery(deliveryPacket)
                    resolveOutgoingRetry(deliveryPacket.messageId)
                } catch (e: Exception) {
                    markInboundProcessingFailure("Delivery processing failed", e)
                    return@launchInboundPacketTask
                }
                if (!syncCoordinator.isBatchInProgress()) {
                    repository.updateLastSyncTimestamp(System.currentTimeMillis())
                }
            }
        }
    }

    private fun registerReadHandler() {
        waitPacket(0x07) { packet ->
            val readPacket = packet as PacketRead

            launchInboundPacketTask {
                val repository = getMessageRepository()
                if (repository == null || !repository.isInitialized()) {
                    syncCoordinator.requireResyncAfterAccountInit(
                        "⏳ Read status before account init, scheduling re-sync"
                    )
                    markInboundProcessingFailure("Read packet skipped before account init", null)
                    return@launchInboundPacketTask
                }
                val ownKey = getProtocolPublicKey()
                if (ownKey.isBlank()) {
                    syncCoordinator.requireResyncAfterAccountInit(
                        "⏳ Read status before protocol account init, scheduling re-sync"
                    )
                    markInboundProcessingFailure(
                        "Read packet skipped before protocol account init",
                        null
                    )
                    return@launchInboundPacketTask
                }
                try {
                    repository.handleRead(readPacket)
                } catch (e: Exception) {
                    markInboundProcessingFailure("Read processing failed", e)
                    return@launchInboundPacketTask
                }
                if (!syncCoordinator.isBatchInProgress()) {
                    // Desktop parity:
                    // own direct read sync (from=me,to=peer) does not advance sync cursor.
                    val isOwnDirectReadSync =
                        readPacket.fromPublicKey.trim() == ownKey &&
                            !isGroupDialogKey(readPacket.toPublicKey)
                    if (!isOwnDirectReadSync) {
                        repository.updateLastSyncTimestamp(System.currentTimeMillis())
                    }
                }
            }
        }
    }

    private fun registerDeviceLoginHandler() {
        waitPacket(0x09) { packet ->
            val devicePacket = packet as PacketDeviceNew

            addLog(
                "🔐 DEVICE LOGIN ATTEMPT: ip=${devicePacket.ipAddress}, device=${devicePacket.device.deviceName}, os=${devicePacket.device.deviceOs}"
            )

            launchInboundPacketTask {
                getMessageRepository()?.addDeviceLoginSystemMessage(
                    ipAddress = devicePacket.ipAddress,
                    deviceId = devicePacket.device.deviceId,
                    deviceName = devicePacket.device.deviceName,
                    deviceOs = devicePacket.device.deviceOs
                )
            }
        }
    }

    private fun registerSyncHandler() {
        waitPacket(0x19) { packet ->
            syncCoordinator.handleSyncPacket(packet as PacketSync)
        }
    }

    private fun registerGroupSyncHandler() {
        waitPacket(0x14) { packet ->
            val joinPacket = packet as PacketGroupJoin

            launchInboundPacketTask {
                val repository = getMessageRepository()
                val groups = getGroupRepository()
                val account = repository?.getCurrentAccountKey()
                val privateKey = repository?.getCurrentPrivateKey()
                if (groups == null || account.isNullOrBlank() || privateKey.isNullOrBlank()) {
                    return@launchInboundPacketTask
                }
                try {
                    val result = groups.synchronizeJoinedGroup(
                        accountPublicKey = account,
                        accountPrivateKey = privateKey,
                        packet = joinPacket
                    )
                    if (result?.success == true) {
                        addLog("👥 GROUP synced: ${result.dialogPublicKey}")
                    }
                } catch (e: Exception) {
                    Log.w(tag, "Failed to sync group packet", e)
                }
            }
        }
    }

    private fun registerOnlineStatusHandler() {
        waitPacket(0x05) { packet ->
            val onlinePacket = packet as PacketOnlineState

            scope.launch {
                val repository = getMessageRepository()
                if (repository != null) {
                    onlinePacket.publicKeysState.forEach { item ->
                        val isOnline = item.state == OnlineState.ONLINE
                        repository.updateOnlineStatus(item.publicKey, isOnline)
                    }
                }
            }
        }
    }

    private fun registerTypingHandler() {
        waitPacket(0x0B) { packet ->
            presenceTypingService.handleTypingPacket(packet as PacketTyping) {
                getProtocolPublicKey().ifBlank {
                    getMessageRepository()?.getCurrentAccountKey()?.trim().orEmpty()
                }
            }
        }
    }

    private fun registerDeviceListHandler() {
        waitPacket(0x17) { packet ->
            deviceRuntimeService.handleDeviceList(packet as PacketDeviceList)
        }
    }

    private fun registerSearchHandler() {
        waitPacket(0x03) { packet ->
            val searchPacket = packet as PacketSearch

            scope.launch(Dispatchers.IO) {
                val ownPublicKey =
                    getProtocolPublicKey().ifBlank {
                        getMessageRepository()?.getCurrentAccountKey()?.trim().orEmpty()
                    }

                packetRouter.onSearchPacket(searchPacket) { user ->
                    val normalizedUserPublicKey = user.publicKey.trim()
                    getMessageRepository()?.updateDialogUserInfo(
                        normalizedUserPublicKey,
                        user.title,
                        user.username,
                        user.verified
                    )

                    val ownProfileResolved =
                        ownProfileSyncService.applyOwnProfileFromSearch(
                            ownPublicKey = ownPublicKey,
                            user = user
                        )
                    if (ownProfileResolved) {
                        onOwnProfileResolved(user.publicKey)
                    }
                }
            }
        }
    }

    private fun registerTransportHandler() {
        waitPacket(0x0F) { packet ->
            val transportPacket = packet as PacketRequestTransport
            setTransportServer(transportPacket.transportServer)
        }
    }
}
