package com.rosetta.messenger.network.connection

import android.content.Context
import android.util.Log
import com.rosetta.messenger.data.MessageRepository
import com.rosetta.messenger.network.Packet
import com.rosetta.messenger.network.PacketPushNotification
import com.rosetta.messenger.network.PushNotificationAction
import com.rosetta.messenger.network.PushTokenType
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

class ProtocolPostAuthBootstrapCoordinator(
    private val tag: String,
    private val scope: CoroutineScope,
    private val authBootstrapCoordinator: AuthBootstrapCoordinator,
    private val syncCoordinator: SyncCoordinator,
    private val ownProfileSyncService: OwnProfileSyncService,
    private val deviceRuntimeService: DeviceRuntimeService,
    private val getMessageRepository: () -> MessageRepository?,
    private val getAppContext: () -> Context?,
    private val getProtocolPublicKey: () -> String?,
    private val getProtocolPrivateHash: () -> String?,
    private val sendPacket: (Packet) -> Unit,
    private val requestTransportServer: () -> Unit,
    private val requestUpdateServer: () -> Unit,
    private val addLog: (String) -> Unit,
    private val shortKeyForLog: (String) -> String,
    private val getLastSubscribedToken: () -> String?,
    private val setLastSubscribedToken: (String?) -> Unit
) {
    fun runPostAuthBootstrap(trigger: String) {
        authBootstrapCoordinator.tryRun(
            trigger = trigger,
            canRun = ::canRunPostAuthBootstrap,
            onDeferred = {
                val repositoryAccount =
                    getMessageRepository()?.getCurrentAccountKey()?.let { shortKeyForLog(it) }
                        ?: "<none>"
                val protocolAccount = getProtocolPublicKey()?.let { shortKeyForLog(it) } ?: "<none>"
                addLog(
                    "⏳ AUTH bootstrap deferred trigger=$trigger repo=$repositoryAccount proto=$protocolAccount"
                )
            }
        ) {
            syncCoordinator.markSyncInProgress(false)
            requestTransportServer()
            requestUpdateServer()
            fetchOwnProfile()
            syncCoordinator.requestSynchronize()
            subscribePushTokenIfAvailable()
        }
    }

    fun handleSyncCompletedSideEffects() {
        retryWaitingMessages()
        requestMissingUserInfo()
    }

    fun subscribePushTokenIfAvailable(forceToken: String? = null) {
        val context = getAppContext() ?: return
        val token =
            (forceToken
                ?: context.getSharedPreferences("rosetta_prefs", Context.MODE_PRIVATE)
                    .getString("fcm_token", null))
                ?.trim()
                .orEmpty()
        if (token.isEmpty()) return

        if (token == getLastSubscribedToken()) {
            addLog("🔔 Push token already subscribed this session — skipped")
            return
        }

        val deviceId = deviceRuntimeService.resolvePushDeviceId()
        val subPacket =
            PacketPushNotification().apply {
                notificationsToken = token
                action = PushNotificationAction.SUBSCRIBE
                tokenType = PushTokenType.FCM
                this.deviceId = deviceId
            }
        sendPacket(subPacket)
        setLastSubscribedToken(token)
        addLog("🔔 Push token SUBSCRIBE sent")

        try {
            val dir = File(context.filesDir, "crash_reports")
            if (!dir.exists()) dir.mkdirs()
            val file = File(dir, "fcm_token.txt")
            val ts =
                SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(Date())
            file.writeText(
                "=== FCM TOKEN ===\n\nTimestamp: $ts\nDeviceId: $deviceId\n\nToken:\n$token\n"
            )
        } catch (_: Throwable) {}
    }

    private fun canRunPostAuthBootstrap(): Boolean {
        val repository = getMessageRepository() ?: return false
        if (!repository.isInitialized()) return false
        val repositoryAccount = repository.getCurrentAccountKey()?.trim().orEmpty()
        if (repositoryAccount.isBlank()) return false
        val protocolAccount = getProtocolPublicKey()?.trim().orEmpty()
        if (protocolAccount.isBlank()) return true
        return repositoryAccount.equals(protocolAccount, ignoreCase = true)
    }

    private fun fetchOwnProfile() {
        val packet =
            ownProfileSyncService.buildOwnProfilePacket(
                publicKey = getProtocolPublicKey(),
                privateHash = getProtocolPrivateHash()
            ) ?: return
        sendPacket(packet)
    }

    private fun retryWaitingMessages() {
        scope.launch {
            val repository = getMessageRepository()
            if (repository == null || !repository.isInitialized()) return@launch
            try {
                repository.retryWaitingMessages()
            } catch (e: Exception) {
                Log.e(tag, "retryWaitingMessages failed", e)
            }
        }
    }

    private fun requestMissingUserInfo() {
        scope.launch {
            val repository = getMessageRepository()
            if (repository == null || !repository.isInitialized()) return@launch
            try {
                repository.clearUserInfoRequestCache()
                repository.requestMissingUserInfo()
            } catch (e: Exception) {
                Log.e(tag, "requestMissingUserInfo failed", e)
            }
        }
    }
}
