package com.rosetta.messenger.push

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.rosetta.messenger.MainActivity
import com.rosetta.messenger.R
import com.rosetta.messenger.data.AccountManager
import com.rosetta.messenger.data.PreferencesManager
import com.rosetta.messenger.network.ProtocolManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import java.util.Locale

/**
 * Firebase Cloud Messaging Service для обработки push-уведомлений
 *
 * Обрабатывает:
 * - Получение нового FCM токена
 * - Получение push-уведомлений о новых сообщениях
 * - Отображение уведомлений
 */
class RosettaFirebaseMessagingService : FirebaseMessagingService() {

    private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

    companion object {
        private const val TAG = "RosettaFCM"
        private const val CHANNEL_ID = "rosetta_messages"
        private const val CHANNEL_NAME = "Messages"

        // 🔥 Флаг - приложение в foreground (видимо пользователю)
        @Volatile var isAppInForeground = false

        // Dedup: suppress duplicate pushes from the server within a short window.
        // Key = senderPublicKey (or "__simple__"), Value = timestamp of last shown notification.
        private const val DEDUP_WINDOW_MS = 10_000L
        private val lastNotifTimestamps = java.util.concurrent.ConcurrentHashMap<String, Long>()

        /** Уникальный notification ID для каждого чата (по publicKey) */
        fun getNotificationIdForChat(senderPublicKey: String): Int {
            return senderPublicKey.hashCode() and 0x7FFFFFFF // positive int
        }

        /** Убрать уведомление конкретного чата из шторки */
        fun cancelNotificationForChat(context: Context, senderPublicKey: String) {
            val notificationManager =
                    context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            val normalizedKey = senderPublicKey.trim()
            if (normalizedKey.isNotEmpty()) {
                notificationManager.cancel(getNotificationIdForChat(normalizedKey))
            }
            // Fallback: некоторые серверные payload могут прийти без sender key.
            // Для них используется ID от пустой строки — тоже очищаем при входе в диалог.
            notificationManager.cancel(getNotificationIdForChat(""))
        }
    }

    /** Вызывается когда получен новый FCM токен Отправляем его на сервер через протокол */
    override fun onNewToken(token: String) {
        super.onNewToken(token)

        // Сохраняем токен локально
        saveFcmToken(token)

        // Best-effort: если соединение уже авторизовано — сразу обновляем подписку на push.
        // Используем единую точку отправки в ProtocolManager (с дедупликацией).
        if (ProtocolManager.isAuthenticated()) {
            runCatching { ProtocolManager.subscribePushTokenIfAvailable(forceToken = token) }
        }
    }

    /** Вызывается когда получено push-уведомление */
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        Log.d(TAG, "\u2709\ufe0f onMessageReceived: messageId=${remoteMessage.messageId} from=${remoteMessage.from} data=${remoteMessage.data} notif=${remoteMessage.notification?.body}")

        var handledMessageData = false

        // Обрабатываем data payload
        if (remoteMessage.data.isNotEmpty()) {
            val data = remoteMessage.data
            val type =
                    firstNonBlank(data, "type", "event", "action")
                            ?.lowercase(Locale.ROOT)
                            .orEmpty()
            val senderPublicKey =
                    firstNonBlank(
                            data,
                            "sender_public_key",
                            "from_public_key",
                            "fromPublicKey",
                            "public_key",
                            "publicKey"
                    )
            val senderName =
                    firstNonBlank(data, "sender_name", "from_title", "sender", "title", "name")
                            ?: senderPublicKey?.take(10)
                            ?: "Rosetta"
            val messagePreview =
                    firstNonBlank(data, "message_preview", "message", "text", "body")
                            ?: "New message"

            val isReadEvent = type == "message_read" || type == "read"
            val isMessageEvent =
                    type == "new_message" ||
                            type == "message" ||
                            type == "newmessage" ||
                            type == "msg_new"

            when {
                isMessageEvent -> {
                    showMessageNotification(senderPublicKey, senderName, messagePreview)
                    handledMessageData = true
                }
                isReadEvent -> {
                    handledMessageData = true
                }
                // Fallback for servers sending data-only payload without explicit "type".
                senderPublicKey != null || data.containsKey("message_preview") || data.containsKey("message") || data.containsKey("text") -> {
                    showMessageNotification(senderPublicKey, senderName, messagePreview)
                    handledMessageData = true
                }
            }
        }

        // Обрабатываем notification payload (если есть).
        // Для new_message используем data-ветку выше, чтобы не показывать дубликаты
        // с неуправляемым notification id.
        remoteMessage.notification?.let {
            if (!handledMessageData) {
                showSimpleNotification(it.title ?: "Rosetta", it.body ?: "New message")
            }
        }
    }

    /** Показать уведомление о новом сообщении */
    private fun showMessageNotification(
            senderPublicKey: String?,
            senderName: String,
            messagePreview: String
    ) {
        // 🔥 Не показываем уведомление если приложение открыто
        if (isAppInForeground || !areNotificationsEnabled()) {
            return
        }
        // Dedup: suppress duplicate pushes from the same sender within DEDUP_WINDOW_MS
        val dedupKey = senderPublicKey?.trim().orEmpty().ifEmpty { "__no_sender__" }
        val now = System.currentTimeMillis()
        val lastTs = lastNotifTimestamps[dedupKey]
        if (lastTs != null && now - lastTs < DEDUP_WINDOW_MS) {
            Log.d(TAG, "\ud83d\udeab Dedup BLOCKED notification for key=$dedupKey, delta=${now - lastTs}ms")
            return  // duplicate push — skip
        }
        lastNotifTimestamps[dedupKey] = now
        Log.d(TAG, "\u2705 Showing notification for key=$dedupKey")
        // Desktop parity: suppress notifications during sync (useDialogFiber.ts checks
        // protocolState != ProtocolState.SYNCHRONIZATION before calling notify()).
        if (ProtocolManager.syncInProgress.value) {
            return
        }
        val senderKey = senderPublicKey?.trim().orEmpty()
        if (senderKey.isNotEmpty() && isDialogMuted(senderKey)) {
            return
        }

        createNotificationChannel()

        val notifId = getNotificationIdForChat(senderPublicKey ?: "")

        // Intent для открытия чата
        val intent =
                Intent(this, MainActivity::class.java).apply {
                    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                    putExtra("open_chat", senderPublicKey)
                }

        val pendingIntent =
                PendingIntent.getActivity(
                        this,
                        notifId,
                        intent,
                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                )

        val notification =
                NotificationCompat.Builder(this, CHANNEL_ID)
                        .setSmallIcon(R.drawable.ic_notification)
                        .setContentTitle(senderName)
                        .setContentText(messagePreview)
                        .setPriority(NotificationCompat.PRIORITY_HIGH)
                        .setCategory(NotificationCompat.CATEGORY_MESSAGE)
                        .setAutoCancel(true)
                        .setContentIntent(pendingIntent)
                        .build()

        val notificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(notifId, notification)
    }

    /** Показать простое уведомление */
    private fun showSimpleNotification(title: String, body: String) {
        // 🔥 Не показываем уведомление если приложение открыто
        if (isAppInForeground || !areNotificationsEnabled()) {
            return
        }
        // Dedup: suppress duplicate pushes within DEDUP_WINDOW_MS
        val dedupKey = "__simple__"
        val now = System.currentTimeMillis()
        val lastTs = lastNotifTimestamps[dedupKey]
        if (lastTs != null && now - lastTs < DEDUP_WINDOW_MS) {
            return  // duplicate push — skip
        }
        lastNotifTimestamps[dedupKey] = now

        createNotificationChannel()

        // Deterministic ID — duplicates replace each other instead of stacking
        val notifId = (title + body).hashCode() and 0x7FFFFFFF

        val intent =
                Intent(this, MainActivity::class.java).apply {
                    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                }

        val pendingIntent =
                PendingIntent.getActivity(
                        this,
                        notifId,
                        intent,
                        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
                )

        val notification =
                NotificationCompat.Builder(this, CHANNEL_ID)
                        .setSmallIcon(R.drawable.ic_notification)
                        .setContentTitle(title)
                        .setContentText(body)
                        .setPriority(NotificationCompat.PRIORITY_HIGH)
                        .setAutoCancel(true)
                        .setContentIntent(pendingIntent)
                        .build()

        val notificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(notifId, notification)
    }

    /** Создать notification channel для Android 8+ */
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel =
                    NotificationChannel(
                                    CHANNEL_ID,
                                    CHANNEL_NAME,
                                    NotificationManager.IMPORTANCE_HIGH
                            )
                            .apply {
                                description = "Notifications for new messages"
                                enableVibration(true)
                            }

            val notificationManager =
                    getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    /** Сохранить FCM токен в SharedPreferences */
    private fun saveFcmToken(token: String) {
        val prefs = getSharedPreferences("rosetta_prefs", Context.MODE_PRIVATE)
        prefs.edit().putString("fcm_token", token).apply()
    }

    private fun areNotificationsEnabled(): Boolean {
        return runCatching {
            runBlocking(Dispatchers.IO) {
                PreferencesManager(applicationContext).notificationsEnabled.first()
            }
        }.getOrDefault(true)
    }

    private fun firstNonBlank(data: Map<String, String>, vararg keys: String): String? {
        for (key in keys) {
            val value = data[key]?.trim()
            if (!value.isNullOrEmpty()) return value
        }
        return null
    }

    /** Проверка: замьючен ли диалог для текущего аккаунта */
    private fun isDialogMuted(senderPublicKey: String): Boolean {
        if (senderPublicKey.isBlank()) return false
        return runCatching {
            val accountManager = AccountManager(applicationContext)
            val currentAccount = accountManager.getLastLoggedPublicKey().orEmpty()
            runBlocking(Dispatchers.IO) {
                PreferencesManager(applicationContext).isChatMuted(currentAccount, senderPublicKey)
            }
        }.getOrDefault(false)
    }
}
