package com.rosetta.messenger.ui.chats

import android.app.Activity
import android.content.Context
import android.net.Uri
import android.view.inputmethod.InputMethodManager
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import compose.icons.TablerIcons
import compose.icons.tablericons.*
import com.rosetta.messenger.ui.icons.TelegramIcons
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.hilt.navigation.compose.hiltViewModel
import app.rosette.android.ui.keyboard.KeyboardTransitionCoordinator
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
import com.rosetta.messenger.R
import com.rosetta.messenger.data.ForwardManager
import com.rosetta.messenger.data.GroupRepository
import com.rosetta.messenger.data.MessageRepository
import com.rosetta.messenger.data.PreferencesManager
import com.rosetta.messenger.database.RosettaDatabase
import com.rosetta.messenger.network.AttachmentType
import com.rosetta.messenger.network.CallManager
import com.rosetta.messenger.network.CallPhase
import com.rosetta.messenger.di.ProtocolGateway
import com.rosetta.messenger.network.SearchUser
import com.rosetta.messenger.ui.chats.calls.CallTopBanner
import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.ui.chats.attach.ChatAttachAlert
import com.rosetta.messenger.ui.chats.components.*
import com.rosetta.messenger.ui.chats.VoiceTopMiniPlayer
import com.rosetta.messenger.ui.chats.components.InAppCameraScreen
import com.rosetta.messenger.ui.chats.components.MultiImageEditorScreen
import com.rosetta.messenger.ui.chats.input.*
import com.rosetta.messenger.ui.chats.models.*
import com.rosetta.messenger.ui.chats.utils.*
import com.rosetta.messenger.ui.components.AppleEmojiText
import com.rosetta.messenger.ui.components.AvatarImage
import com.rosetta.messenger.ui.components.VerifiedBadge
import com.rosetta.messenger.ui.components.metaball.DevicePerformanceClass
import com.rosetta.messenger.ui.components.metaball.PerformanceClass
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import com.rosetta.messenger.ui.settings.ThemeWallpapers
import com.rosetta.messenger.ui.utils.NavigationModeUtils
import com.rosetta.messenger.ui.utils.SystemBarsStyleUtils
import com.rosetta.messenger.utils.MediaUtils
import java.text.SimpleDateFormat
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

private val groupMembersCountCache = java.util.concurrent.ConcurrentHashMap<String, Int>()
private val groupAdminKeysCache = java.util.concurrent.ConcurrentHashMap<String, Set<String>>()
private const val GROUP_MESSAGE_STACK_TIME_DIFF_MS = 5 * 60_000L
private const val DIRECT_MESSAGE_STACK_TIME_DIFF_MS = 60_000L
private const val MESSAGE_SKELETON_VISIBILITY_DELAY_MS = 180L

private enum class ChatHeaderMode {
        NORMAL,
        SEARCH,
        SELECTION
}

private fun buildDialogKeyForSearch(account: String, opponent: String): String {
        val normalizedAccount = account.trim()
        val normalizedOpponent = opponent.trim()
        val normalizedLower = normalizedOpponent.lowercase(Locale.ROOT)
        val isGroup =
                normalizedLower.startsWith("#group:") || normalizedLower.startsWith("group:")
        if (isGroup) return normalizedOpponent
        if (normalizedAccount == normalizedOpponent) return normalizedAccount
        return if (normalizedAccount < normalizedOpponent) {
                "$normalizedAccount:$normalizedOpponent"
        } else {
                "$normalizedOpponent:$normalizedAccount"
        }
}

private fun isSameLocalDay(firstTimestampMs: Long, secondTimestampMs: Long): Boolean {
        val firstCalendar =
                java.util.Calendar.getInstance().apply {
                        timeInMillis = firstTimestampMs
                }
        val secondCalendar =
                java.util.Calendar.getInstance().apply {
                        timeInMillis = secondTimestampMs
                }
        return firstCalendar.get(java.util.Calendar.ERA) ==
                secondCalendar.get(java.util.Calendar.ERA) &&
                firstCalendar.get(java.util.Calendar.YEAR) ==
                        secondCalendar.get(java.util.Calendar.YEAR) &&
                firstCalendar.get(java.util.Calendar.DAY_OF_YEAR) ==
                        secondCalendar.get(java.util.Calendar.DAY_OF_YEAR)
}

private data class IncomingRunAvatarAccumulator(
        val senderPublicKey: String,
        val senderDisplayName: String,
        var minTopPx: Float,
        var maxBottomPx: Float
)

private data class IncomingRunAvatarOverlay(
        val runHeadIndex: Int,
        val senderPublicKey: String,
        val senderDisplayName: String,
        val topPx: Float
)

private data class IncomingRunAvatarUiState(
        val showOnRunHeads: Set<Int>,
        val showOnRunTails: Set<Int>,
        val overlays: List<IncomingRunAvatarOverlay>
)

private val copiedTimeRegex = Regex("^\\d{1,2}:\\d{2}(?:\\s?[AaPp][Mm])?$")

private fun sanitizeCopyText(rawText: String, senderName: String): String {
        if (rawText.isBlank()) return ""

        val normalizedSender = senderName.trim()
        val lines =
                rawText
                        .replace("\r\n", "\n")
                        .split('\n')
                        .map { it.trim() }
                        .filter { it.isNotEmpty() }
                        .toMutableList()

        while (lines.isNotEmpty()) {
                val first = lines.first()
                val isSenderLine =
                        normalizedSender.isNotBlank() &&
                                first.equals(normalizedSender, ignoreCase = true)
                val isForwardHeader = first.startsWith("Forwarded from ", ignoreCase = true)
                if (!isSenderLine && !isForwardHeader) break
                lines.removeAt(0)
        }

        while (lines.isNotEmpty() && copiedTimeRegex.matches(lines.last())) {
                lines.removeAt(lines.lastIndex)
        }

        return lines.joinToString("\n").trim()
}

private fun extractCopyableMessageText(message: ChatMessage): String {
        val directText = sanitizeCopyText(message.text, message.senderName)
        if (directText.isNotEmpty()) {
                return directText
        }

        val forwardedText =
                message.forwardedMessages
                        .mapNotNull { forwarded ->
                                sanitizeCopyText(
                                                rawText = forwarded.text,
                                                senderName =
                                                        forwarded.forwardedFromName
                                                                .ifBlank { forwarded.senderName }
                                        )
                                        .takeIf { it.isNotEmpty() }
                        }
                        .joinToString("\n\n")
        if (forwardedText.isNotEmpty()) {
                return forwardedText
        }

        val replyText =
                message.replyData
                        ?.let { reply ->
                                sanitizeCopyText(
                                        rawText = reply.text,
                                        senderName = reply.forwardedFromName.ifBlank { reply.senderName }
                                )
                        }
                        .orEmpty()
        if (replyText.isNotEmpty()) {
                return replyText
        }

        return ""
}

@Composable
private fun GroupRejoinRequiredState(
        isDarkTheme: Boolean,
        hasChatWallpaper: Boolean,
        modifier: Modifier = Modifier
) {
        val titleColor = if (isDarkTheme || hasChatWallpaper) Color.White else Color(0xFF1C1C1E)
        val subtitleColor = titleColor.copy(alpha = 0.72f)

        val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.speech))
        val progress by
                animateLottieCompositionAsState(
                        composition = composition,
                        iterations = LottieConstants.IterateForever
                )

        Column(
                modifier = modifier.fillMaxSize().padding(horizontal = 32.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
        ) {
                LottieAnimation(
                        composition = composition,
                        progress = { progress },
                        modifier = Modifier.size(120.dp)
                )
                Spacer(modifier = Modifier.height(16.dp))
                Text(
                        text = "Join Group Again",
                        fontSize = 17.sp,
                        fontWeight = FontWeight.SemiBold,
                        color = titleColor,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.fillMaxWidth()
                )
                Spacer(modifier = Modifier.height(8.dp))
                Text(
                        text = "Group sync key is missing. Rejoin this group to load messages.",
                        fontSize = 14.sp,
                        color = subtitleColor,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.fillMaxWidth()
                )
        }
}

@OptIn(
        ExperimentalMaterial3Api::class,
        androidx.compose.foundation.ExperimentalFoundationApi::class,
        androidx.compose.ui.ExperimentalComposeUiApi::class,
        FlowPreview::class
)
@Composable
fun ChatDetailScreen(
        user: SearchUser,
        onBack: () -> Unit,
        onNavigateToChat: (SearchUser) -> Unit,
        protocolGateway: ProtocolGateway,
        preferencesManager: PreferencesManager,
        messageRepository: MessageRepository,
        groupRepository: GroupRepository,
        onCallClick: (SearchUser) -> Unit = {},
        onUserProfileClick: (SearchUser) -> Unit = {},
        onGroupInfoClick: (SearchUser) -> Unit = {},
        currentUserPublicKey: String,
        currentUserPrivateKey: String,
        currentUserName: String = "",
        currentUserUsername: String = "",
        totalUnreadFromOthers: Int = 0,
        isDarkTheme: Boolean,
        chatWallpaperId: String = "",
        avatarRepository: AvatarRepository? = null,
        onImageViewerChanged: (Boolean) -> Unit = {},
        isCallActive: Boolean = false,
        onOpenCallOverlay: () -> Unit = {},
        onSelectionModeChange: (Boolean) -> Unit = {},
        registerClearSelection: (() -> Unit) -> Unit = {},
        onVoiceWaveGestureChanged: (Boolean) -> Unit = {}
) {
        val viewModel: ChatViewModel = hiltViewModel(key = "chat_${user.publicKey}")
        val messagesViewModel = remember(viewModel) { viewModel.messagesViewModel }
        val typingViewModel = remember(viewModel) { viewModel.typingViewModel }
        val voiceRecordingViewModel = remember(viewModel) { viewModel.voiceRecordingViewModel }
        val attachmentsViewModel = remember(viewModel) { viewModel.attachmentsViewModel }
        val context = LocalContext.current
        val hasNativeNavigationBar = remember(context) {
                com.rosetta.messenger.ui.utils.NavigationModeUtils.hasNativeNavigationBar(context)
        }
        val scope = rememberCoroutineScope()
        val keyboardController = LocalSoftwareKeyboardController.current
        val focusManager = LocalFocusManager.current
        val clipboardManager = LocalClipboardManager.current
        val database = RosettaDatabase.getDatabase(context)
        val searchIndexDao = remember(database) { database.messageSearchIndexDao() }
        val hapticFeedback = LocalHapticFeedback.current

        // 🔇 Mute state — read from PreferencesManager
        val isChatMuted = remember { mutableStateOf(false) }
        LaunchedEffect(currentUserPublicKey, user.publicKey) {
                if (currentUserPublicKey.isNotBlank()) {
                        isChatMuted.value = preferencesManager.isChatMuted(currentUserPublicKey, user.publicKey)
                }
        }

        // UI Theme
        val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFF2F2F7)
        val chatWallpaperResId = remember(chatWallpaperId) { ThemeWallpapers.drawableResOrNull(chatWallpaperId) }
        val hasChatWallpaper = remember(chatWallpaperId, chatWallpaperResId) {
                chatWallpaperId.isNotBlank() || chatWallpaperResId != null
        }
        val textColor = if (isDarkTheme) Color.White else Color.Black
        val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF8E8E93)
        val dateHeaderTextColor = if (isDarkTheme || hasChatWallpaper) Color.White else secondaryTextColor
        val dateHeaderBackgroundColor =
                if (isDarkTheme || hasChatWallpaper) Color(0x80212121) else Color(0xCCE8E8ED)
        val headerIconColor = Color.White

        // 🔥 Keyboard & Emoji Coordinator
        val coordinator = remember { KeyboardTransitionCoordinator() }
        val view = LocalView.current

        // 🎨 Window reference для управления статус баром
        val window = remember { (view.context as? Activity)?.window }

        // 🔥 Emoji picker state
        var showEmojiPicker by remember { mutableStateOf(false) }

        // 🔥 Media picker state (gallery bottom sheet)
        var showMediaPicker by remember { mutableStateOf(false) }

        // 🔥 List state с начальной позицией 0 (самое новое сообщение)
        val listState = rememberLazyListState(initialFirstVisibleItemIndex = 0)

        // 🔥 Состояние для отслеживания отправки сообщения (блокирует scroll-to-top)
        var isSendingMessage by remember { mutableStateOf(false) }

        // 🔥 Отслеживание ручной прокрутки
        var wasManualScroll by remember { mutableStateOf(false) }

        // 🔥 Подсветка сообщения при клике на reply
        var highlightedMessageId by remember { mutableStateOf<String?>(null) }

        // 🔥 MESSAGE SELECTION STATE - для Reply/Forward
        var selectedMessages by remember { mutableStateOf<Set<String>>(emptySet()) }
        val isSelectionMode = selectedMessages.isNotEmpty()
        var isVoiceWaveGestureActive by remember { mutableStateOf(false) }

        // Notify parent about selection mode changes so it can intercept swipe-back
        LaunchedEffect(isSelectionMode) { onSelectionModeChange(isSelectionMode) }
        LaunchedEffect(isVoiceWaveGestureActive) {
                onVoiceWaveGestureChanged(isVoiceWaveGestureActive)
        }
        // Register selection-clear callback so parent can cancel selection on swipe-back
        DisposableEffect(Unit) {
            registerClearSelection { selectedMessages = emptySet() }
            onDispose {
                    registerClearSelection {}
                    onVoiceWaveGestureChanged(false)
            }
        }
        // После long press AndroidView текста может прислать tap на отпускание.
        // В этом окне игнорируем tap по этому же сообщению, чтобы selection не снимался.
        var longPressSuppressedMessageId by remember { mutableStateOf<String?>(null) }
        var longPressSuppressUntilMs by remember { mutableLongStateOf(0L) }

        // 🔤 TEXT SELECTION - Telegram-style character-level selection
        val textSelectionHelper = remember { com.rosetta.messenger.ui.chats.components.TextSelectionHelper() }
        LaunchedEffect(Unit) { textSelectionHelper.setMagnifierView(view) }

        // 💬 MESSAGE CONTEXT MENU STATE
        var contextMenuMessage by remember { mutableStateOf<ChatMessage?>(null) }
        var showContextMenu by remember { mutableStateOf(false) }
        var contextMenuIsPinned by remember { mutableStateOf(false) }

        // 📌 PINNED MESSAGES
        val pinnedMessages by messagesViewModel.pinnedMessages.collectAsState()
        val pinnedMessagePreviews by messagesViewModel.pinnedMessagePreviews.collectAsState()
        val currentPinnedIndex by messagesViewModel.currentPinnedIndex.collectAsState()
        var isPinnedBannerDismissed by remember { mutableStateOf(false) }

        // Логирование изменений selection mode
        LaunchedEffect(isSelectionMode, selectedMessages.size) {}

        // Сброс выделения при начале звонка
        LaunchedEffect(isCallActive) {
                if (isCallActive) {
                        selectedMessages = emptySet()
                }
        }

        // 🔥 Backup: если клавиатура ещё открыта когда selection mode активировался
        // (клавиатура уже должна быть закрыта в onLongClick, это только backup)
        LaunchedEffect(isSelectionMode) {
                if (isSelectionMode) {
                        // Backup закрытие клавиатуры (основное в onLongClick)
                        keyboardController?.hide()
                }
        }

        val hideInputOverlays: () -> Unit = {
                val imm =
                        context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                imm.hideSoftInputFromWindow(view.windowToken, 0)
                window?.let { win ->
                        androidx.core.view.WindowCompat.getInsetsController(win, view)
                                .hide(androidx.core.view.WindowInsetsCompat.Type.ime())
                }
                keyboardController?.hide()
                focusManager.clearFocus(force = true)
                showEmojiPicker = false
        }

        // 🔥 Принудительное закрытие экрана (используется в explicit actions вроде Delete chat)
        val hideKeyboardAndBack: () -> Unit = {
                hideInputOverlays()
                onBack()
        }
        // 🔥 Поведение как у нативного Android back:
        // сначала закрываем IME/emoji, и только следующим back выходим из чата.
        val handleBackWithInputPriority: () -> Unit = {
                val imeVisible =
                        androidx.core.view.ViewCompat.getRootWindowInsets(view)
                                ?.isVisible(androidx.core.view.WindowInsetsCompat.Type.ime()) == true
                val hasInputOverlay =
                        showEmojiPicker ||
                                coordinator.isEmojiBoxVisible ||
                                coordinator.isKeyboardVisible ||
                                imeVisible

                if (hasInputOverlay) {
                        hideInputOverlays()
                } else {
                        onBack()
                }
        }

        // Определяем это Saved Messages или обычный чат
        val isSavedMessages = user.publicKey == currentUserPublicKey
        val isGroupChat = user.publicKey.trim().startsWith("#group:")
        val chatTitle =
                if (isSavedMessages) "Saved Messages"
                else user.title.ifEmpty { user.publicKey.take(10) }
        var chatHeaderVerified by
                remember(user.publicKey, user.verified) {
                        mutableIntStateOf(user.verified.coerceAtLeast(0))
                }

        LaunchedEffect(
                user.publicKey,
                user.verified,
                currentUserPublicKey,
                isSavedMessages,
                isGroupChat
        ) {
                chatHeaderVerified = user.verified.coerceAtLeast(0)

                if (isSavedMessages || isGroupChat || currentUserPublicKey.isBlank()) {
                        return@LaunchedEffect
                }

                val normalizedPublicKey = user.publicKey.trim()
                if (normalizedPublicKey.isBlank()) return@LaunchedEffect

                val cachedVerified =
                        protocolGateway.getCachedUserInfo(normalizedPublicKey)?.verified ?: 0
                if (cachedVerified > chatHeaderVerified) {
                        chatHeaderVerified = cachedVerified
                }

                val localVerified =
                        withContext(Dispatchers.IO) {
                                runCatching {
                                                database
                                                        .dialogDao()
                                                        .getDialog(currentUserPublicKey, normalizedPublicKey)
                                                        ?.verified ?: 0
                                        }
                                        .getOrDefault(0)
                        }
                if (localVerified > chatHeaderVerified) {
                        chatHeaderVerified = localVerified
                }

                val resolvedVerified =
                        runCatching {
                                        messagesViewModel.resolveUserForProfile(normalizedPublicKey)?.verified
                                                ?: 0
                                }
                                .getOrDefault(0)
                if (resolvedVerified > chatHeaderVerified) {
                        chatHeaderVerified = resolvedVerified
                }
        }

        val openDialogInfo: () -> Unit = {
                hideInputOverlays()
                showContextMenu = false
                contextMenuMessage = null
                if (isGroupChat) {
                        onGroupInfoClick(user)
                } else {
                        onUserProfileClick(user)
                }
        }

        // 📨 Forward: показывать ли выбор чата
        var showForwardPicker by remember { mutableStateOf(false) }

        // 📸 Image Viewer state with Telegram-style shared element animation
        var showImageViewer by remember { mutableStateOf(false) }
        var imageViewerInitialIndex by remember { mutableStateOf(0) }
        var imageViewerSourceBounds by remember { mutableStateOf<ImageSourceBounds?>(null) }
        var imageViewerImages by remember { mutableStateOf<List<ViewableImage>>(emptyList()) }
        var simplePickerPreviewUri by remember { mutableStateOf<Uri?>(null) }
        var simplePickerPreviewSourceThumb by remember { mutableStateOf<ThumbnailPosition?>(null) }
        var simplePickerPreviewCaption by remember { mutableStateOf("") }
        var simplePickerPreviewGalleryUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
        var simplePickerPreviewInitialIndex by remember { mutableIntStateOf(0) }

        // 🎨 Управление статус баром.
        // Важно: когда открыт media/camera overlay, статус-баром управляет сам overlay.
        // Иначе ChatDetail может перетереть затемнение пикера в прозрачный.
        if (!view.isInEditMode) {
                SideEffect {
                        if (showImageViewer) {
                                SystemBarsStyleUtils.applyFullscreenDark(window, view)
                        } else {
                                val isOverlayControllingSystemBars = showMediaPicker

                                if (!isOverlayControllingSystemBars && window != null) {
                                        val ic = androidx.core.view.WindowCompat.getInsetsController(window, view)
                                        window.statusBarColor = android.graphics.Color.TRANSPARENT
                                        ic.isAppearanceLightStatusBars = false
                                        NavigationModeUtils.applyNavigationBarVisibility(
                                                window = window,
                                                insetsController = ic,
                                                context = context,
                                                isDarkTheme = isDarkTheme
                                        )
                                }
                        }
                }
        }

        DisposableEffect(Unit) {
                onDispose {
                        // Восстановить белые иконки статус-бара для chat list header
                        if (window != null) {
                                val ic = androidx.core.view.WindowCompat.getInsetsController(window, view)
                                window.statusBarColor = android.graphics.Color.TRANSPARENT
                                ic.isAppearanceLightStatusBars = false
                                NavigationModeUtils.applyNavigationBarVisibility(
                                        window = window,
                                        insetsController = ic,
                                        context = context,
                                        isDarkTheme = isDarkTheme
                                )
                        }
                }
        }

        // 📷 Состояние для flow камеры: фото → редактор с caption → отправка
        var pendingCameraPhotoUri by remember {
                mutableStateOf<Uri?>(null)
        } // Фото для редактирования
        var pendingCameraPhotoCaption by remember { mutableStateOf("") }

        // 📷 Показать встроенную камеру (без системного превью)
        var showInAppCamera by remember { mutableStateOf(false) }

        // 🖼 Состояние для multi-image editor (галерея)
        var pendingGalleryImages by remember { mutableStateOf<List<Uri>>(emptyList()) }

        // Триггер для возврата фокуса на инпут после отправки фото из редактора
        var inputFocusTrigger by remember { mutableStateOf(0) }

        // 📷 При входе в экран камеры гарантированно закрываем IME и снимаем фокус с инпута.
        // Это защищает от кейсов, когда keyboardController не успевает из-за анимаций overlay.
        LaunchedEffect(showInAppCamera) {
                if (showInAppCamera) {
                        val imm =
                                context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                        repeat(8) {
                                imm.hideSoftInputFromWindow(view.windowToken, 0)
                                window?.let { win ->
                                        androidx.core.view.WindowCompat.getInsetsController(win, view)
                                                .hide(androidx.core.view.WindowInsetsCompat.Type.ime())
                                }
                                view.findFocus()?.clearFocus()
                                (context as? Activity)?.currentFocus?.clearFocus()
                                keyboardController?.hide()
                                focusManager.clearFocus(force = true)
                                showEmojiPicker = false
                                delay(40)
                        }
                }
        }

        // Блокируем swipe-back родительского экрана, пока открыт fullscreen/media overlay.
        val shouldLockParentSwipeBack by
                remember(
                        showImageViewer,
                        showMediaPicker,
                        showEmojiPicker,
                        textSelectionHelper.isActive,
                        textSelectionHelper.movingHandle,
                        pendingCameraPhotoUri,
                        pendingGalleryImages,
                        showInAppCamera,
                        simplePickerPreviewUri
                ) {
                        derivedStateOf {
                                showImageViewer ||
                                        showMediaPicker ||
                                        showEmojiPicker ||
                                        textSelectionHelper.isActive ||
                                        textSelectionHelper.movingHandle ||
                                        pendingCameraPhotoUri != null ||
                                        pendingGalleryImages.isNotEmpty() ||
                                        showInAppCamera ||
                                        simplePickerPreviewUri != null
                        }
                }

        LaunchedEffect(shouldLockParentSwipeBack) {
                onImageViewerChanged(shouldLockParentSwipeBack)
        }

        LaunchedEffect(simplePickerPreviewUri) {
                if (simplePickerPreviewUri != null) {
                        showContextMenu = false
                        contextMenuMessage = null
                }
        }

        DisposableEffect(Unit) {
                onDispose { onImageViewerChanged(false) }
        }

        // �📄 File picker launcher
        val filePickerLauncher =
                rememberLauncherForActivityResult(
                        contract = ActivityResultContracts.GetContent()
                ) { uri ->
                        if (uri != null) {
                                scope.launch {
                                        val fileName = MediaUtils.getFileName(context, uri)
                                        val fileSize = MediaUtils.getFileSize(context, uri)

                                        // Проверяем размер файла
                                        if (fileSize > MediaUtils.MAX_FILE_SIZE_MB * 1024 * 1024) {
                                                android.widget.Toast.makeText(
                                                                context,
                                                                "Файл слишком большой (макс. ${MediaUtils.MAX_FILE_SIZE_MB} МБ)",
                                                                android.widget.Toast.LENGTH_LONG
                                                        )
                                                        .show()
                                                return@launch
                                        }

                                        val base64 = MediaUtils.uriToBase64File(context, uri)
                                        if (base64 != null) {
                                                attachmentsViewModel.sendFileMessage(
                                                        base64,
                                                        fileName,
                                                        fileSize
                                                )
                                        }
                                }
                        }
                }

        // 📨 Forward: список диалогов для выбора (загружаем из базы)
        val chatsListViewModel: ChatsListViewModel = hiltViewModel()
        val dialogsList by chatsListViewModel.dialogs.collectAsState()
        val groupMembersCacheKey =
                remember(user.publicKey, currentUserPublicKey) {
                        "${currentUserPublicKey.trim()}::${user.publicKey.trim()}"
                }
        var groupAdminKeys by remember(groupMembersCacheKey) {
                mutableStateOf(groupAdminKeysCache[groupMembersCacheKey] ?: emptySet())
        }
        var groupMembersCount by remember(groupMembersCacheKey) {
                mutableStateOf<Int?>(groupMembersCountCache[groupMembersCacheKey])
        }
        var mentionCandidates by remember(user.publicKey, currentUserPublicKey) {
                mutableStateOf<List<MentionCandidate>>(emptyList())
        }

        // 📨 Forward: инициализируем ChatsListViewModel для получения списка диалогов
        LaunchedEffect(currentUserPublicKey, currentUserPrivateKey) {
                if (currentUserPublicKey.isNotEmpty() && currentUserPrivateKey.isNotEmpty()) {
                        chatsListViewModel.setAccount(currentUserPublicKey, currentUserPrivateKey)
                }
        }

        LaunchedEffect(isGroupChat, user.publicKey, currentUserPublicKey) {
                if (!isGroupChat || user.publicKey.isBlank() || currentUserPublicKey.isBlank()) {
                        groupAdminKeys = emptySet()
                        groupMembersCount = null
                        mentionCandidates = emptyList()
                        return@LaunchedEffect
                }

                val cachedMembersCount = groupMembersCountCache[groupMembersCacheKey]
                groupMembersCount = cachedMembersCount
                val cachedAdminKeys = groupAdminKeysCache[groupMembersCacheKey]
                if (!cachedAdminKeys.isNullOrEmpty()) {
                        groupAdminKeys = cachedAdminKeys
                }

                val members = withContext(Dispatchers.IO) {
                        groupRepository.requestGroupMembers(user.publicKey)
                }
                if (members == null) {
                        mentionCandidates = emptyList()
                        return@LaunchedEffect
                }
                val normalizedMembers =
                        members.map { it.trim() }
                                .filter { it.isNotBlank() }
                                .distinct()
                groupMembersCount = normalizedMembers.size
                if (normalizedMembers.isNotEmpty()) {
                        groupMembersCountCache[groupMembersCacheKey] = normalizedMembers.size
                }

                val adminKey = normalizedMembers.firstOrNull().orEmpty()
                groupAdminKeys =
                        if (adminKey.isBlank()) emptySet() else setOf(adminKey)
                if (groupAdminKeys.isNotEmpty()) {
                        groupAdminKeysCache[groupMembersCacheKey] = groupAdminKeys
                }

                mentionCandidates =
                        withContext(Dispatchers.IO) {
                                normalizedMembers
                                        .filter { !it.equals(currentUserPublicKey.trim(), ignoreCase = true) }
                                        .mapNotNull { memberKey ->
                                                val resolvedUser = messagesViewModel.resolveUserForProfile(memberKey) ?: return@mapNotNull null
                                                val normalizedUsername = resolvedUser.username.trim().trimStart('@')
                                                MentionCandidate(
                                                        username = normalizedUsername,
                                                        title = resolvedUser.title.ifBlank { normalizedUsername },
                                                        publicKey = memberKey
                                                )
                                        }
                                        .sortedBy { it.username.lowercase(Locale.ROOT) }
                        }
        }

        // Состояние выпадающего меню
        var showMenu by remember { mutableStateOf(false) }
        var showChatOpenMetricsDialog by remember { mutableStateOf(false) }
        var showDeleteConfirm by remember { mutableStateOf(false) }
        var showBlockConfirm by remember { mutableStateOf(false) }
        var showUnblockConfirm by remember { mutableStateOf(false) }
        var isInChatSearchMode by rememberSaveable(user.publicKey) { mutableStateOf(false) }
        var inChatSearchQuery by rememberSaveable(user.publicKey) { mutableStateOf("") }
        var inChatSearchResultIds by remember(user.publicKey) { mutableStateOf<List<String>>(emptyList()) }
        var inChatSearchResultIndex by rememberSaveable(user.publicKey) { mutableIntStateOf(-1) }
        val searchFieldFocusRequester = remember(user.publicKey) { FocusRequester() }
        val searchDialogKey =
                remember(currentUserPublicKey, user.publicKey) {
                        buildDialogKeyForSearch(
                                account = currentUserPublicKey,
                                opponent = user.publicKey
                        )
                }
        // Наблюдаем за статусом блокировки в реальном времени через Flow
        val isBlocked by
                database.blacklistDao()
                        .observeUserBlocked(user.publicKey, currentUserPublicKey)
                        .collectAsState(initial = false)

        // Подключаем к ViewModel
        val messages by messagesViewModel.messages.collectAsState()
        val isTyping by typingViewModel.opponentTyping.collectAsState()
        val typingDisplayName by typingViewModel.typingDisplayName.collectAsState()
        val typingDisplayPublicKey by typingViewModel.typingDisplayPublicKey.collectAsState()
        @Suppress("UNUSED_VARIABLE")
        val isLoadingMore by messagesViewModel.isLoadingMore.collectAsState()
        val rawIsOnline by typingViewModel.opponentOnline.collectAsState()
        // If typing, the user is obviously online — never show "offline" while typing
        val isOnline = rawIsOnline || isTyping
        val isLoading by messagesViewModel.isLoading.collectAsState() // 🔥 Для скелетона
        val chatOpenMetrics by messagesViewModel.chatOpenMetrics.collectAsState()
        val performanceClass =
                remember(context.applicationContext) {
                        DevicePerformanceClass.get(context.applicationContext)
                }
        val useLightweightColdOpen =
                remember(performanceClass, chatOpenMetrics.firstListLayoutMs) {
                        (performanceClass == PerformanceClass.LOW ||
                                performanceClass == PerformanceClass.AVERAGE) &&
                                chatOpenMetrics.firstListLayoutMs == null
                }
        val groupRequiresRejoin by messagesViewModel.groupRequiresRejoin.collectAsState()
        val showMessageSkeleton by
                produceState(initialValue = false, key1 = isLoading) {
                        if (!isLoading) {
                                value = false
                                return@produceState
                        }
                        delay(MESSAGE_SKELETON_VISIBILITY_DELAY_MS)
                        value = isLoading
                }

        // �🔥 Reply/Forward state
        val replyMessages by messagesViewModel.replyMessages.collectAsState()
        val isForwardMode by messagesViewModel.isForwardMode.collectAsState()
        val pendingDeleteIds by messagesViewModel.pendingDeleteIds.collectAsState()

        // Avatar-сообщения не должны попадать в selection ни при каких условиях.
        val avatarMessageIds =
                remember(messages) {
                        messages.filter { msg ->
                                msg.attachments.any { it.type == AttachmentType.AVATAR }
                        }.map { it.id }.toSet()
                }

        LaunchedEffect(avatarMessageIds) {
                if (selectedMessages.isNotEmpty()) {
                        val filteredSelection = selectedMessages - avatarMessageIds
                        if (filteredSelection != selectedMessages) {
                                selectedMessages = filteredSelection
                        }
                }
        }

        val toggleMessageSelection: (messageId: String, canSelect: Boolean) -> Unit =
                { messageId, canSelect ->
                        if (canSelect) {
                                selectedMessages =
                                        if (selectedMessages.contains(messageId)) {
                                                selectedMessages - messageId
                                        } else {
                                                selectedMessages + messageId
                                        }
                        }
                }

        // Long press должен только включать selection для сообщения (идемпотентно),
        // иначе при двойном колбэке (text + bubble) сообщение мгновенно "откатывается".
        val selectMessageOnLongPress: (messageId: String, canSelect: Boolean) -> Unit =
                { messageId, canSelect ->
                        textSelectionHelper.clear()
                        if (canSelect && !selectedMessages.contains(messageId)) {
                                selectedMessages = selectedMessages + messageId
                        }
                }
        val suppressTapAfterLongPress: (messageId: String) -> Unit =
                { messageId ->
                        longPressSuppressedMessageId = messageId
                        longPressSuppressUntilMs = System.currentTimeMillis() + 350L
                }
        val shouldIgnoreTapAfterLongPress: (messageId: String) -> Boolean =
                { messageId ->
                        val now = System.currentTimeMillis()
                        val isSuppressed =
                                longPressSuppressedMessageId == messageId &&
                                        now <= longPressSuppressUntilMs
                        if (isSuppressed || now > longPressSuppressUntilMs) {
                                longPressSuppressedMessageId = null
                        }
                        isSuppressed
                }

        // 🔥 ПАГИНАЦИЯ: Загружаем старые сообщения при прокрутке вверх
        // NOTE: Не нужен ручной scrollToItem - LazyColumn с reverseLayout=true
        // автоматически сохраняет позицию благодаря стабильным ключам (key = message.id)
        // reverseLayout=true означает что index 0 - это новое сообщение внизу,
        // а большие индексы - старые сообщения вверху
        // Используем snapshotFlow с debounce для плавной пагинации без прерывания скролла
        LaunchedEffect(listState) {
                snapshotFlow {
                        val layoutInfo = listState.layoutInfo
                        val totalItems = layoutInfo.totalItemsCount
                        val lastVisibleItemIndex =
                                layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
                        Pair(lastVisibleItemIndex, totalItems)
                }
                        .distinctUntilChanged()
                        .debounce(100) // 🔥 Debounce чтобы не триггерить на каждый фрейм скролла
                        .collect { (lastVisible, total) ->
                                // Загружаем когда осталось 5 элементов до конца и не идёт загрузка
                                if (total > 0 &&
                                                lastVisible >= total - 5 &&
                                                !messagesViewModel.isLoadingMore.value
                                ) {
                                        messagesViewModel.loadMoreMessages()
                                }
                        }
        }

        // 🔤 Сброс текстового выделения при скролле
        LaunchedEffect(listState.isScrollInProgress) {
                if (listState.isScrollInProgress && textSelectionHelper.isActive) {
                        textSelectionHelper.clear()
                }
        }

        // 🔥 Display reply messages - получаем полную информацию о сообщениях для reply
        val displayReplyMessages =
                remember(replyMessages, messages) {
                        replyMessages.mapNotNull { replyMsg ->
                                messages.find { it.id == replyMsg.messageId }?.let { chatMsg ->
                                        ChatViewModel.ReplyMessage(
                                                messageId = chatMsg.id,
                                                text = chatMsg.text,
                                                timestamp = chatMsg.timestamp.time,
                                                isOutgoing = chatMsg.isOutgoing,
                                                publicKey =
                                                        chatMsg.senderPublicKey.ifEmpty {
                                                                if (chatMsg.isOutgoing)
                                                                        currentUserPublicKey
                                                                else user.publicKey
                                                        },
                                                senderName = chatMsg.senderName,
                                                attachments = chatMsg.attachments
                                        )
                                }
                        }
                }

        // � Текст текущего pinned сообщения для баннера
        val currentPinnedMessagePreview =
                remember(pinnedMessages, currentPinnedIndex, messages, pinnedMessagePreviews) {
            if (pinnedMessages.isEmpty()) ""
            else {
                val idx = currentPinnedIndex.coerceIn(0, pinnedMessages.size - 1)
                val pinnedMsgId = pinnedMessages[idx].messageId
                pinnedMessagePreviews[pinnedMsgId]
                        ?: messages.find { it.id == pinnedMsgId }?.text
                        ?: "Pinned message"
            }
        }

        // 📌 Сброс dismissed при изменении pinned messages (когда добавляют новый pin)
        LaunchedEffect(pinnedMessages.size) {
            if (pinnedMessages.isNotEmpty()) {
                isPinnedBannerDismissed = false
            }
        }

        // �🔥 messagesWithDates — pre-computed in ViewModel on Dispatchers.Default
        // (dedup + sort + date headers off the main thread)
        val messagesWithDates by messagesViewModel.messagesWithDates.collectAsState()
        val resolveSenderPublicKey: (ChatMessage?) -> String =
                remember(isGroupChat, currentUserPublicKey, user.publicKey) {
                        { msg ->
                                when {
                                        msg == null -> ""
                                        msg.senderPublicKey.isNotBlank() -> msg.senderPublicKey.trim()
                                        msg.isOutgoing -> currentUserPublicKey.trim()
                                        isGroupChat -> ""
                                        else -> user.publicKey.trim()
                                }
                        }
                }
        val resolveSenderIdentity: (ChatMessage?) -> String =
                remember(isGroupChat, currentUserPublicKey, user.publicKey) {
                        { msg ->
                                when {
                                        msg == null -> ""
                                        msg.isOutgoing ->
                                                "out:${currentUserPublicKey.trim().lowercase(Locale.ROOT)}"
                                        msg.senderPublicKey.isNotBlank() ->
                                                "in:${msg.senderPublicKey.trim().lowercase(Locale.ROOT)}"
                                        isGroupChat && msg.senderName.isNotBlank() ->
                                                "name:${msg.senderName.trim().lowercase(Locale.ROOT)}"
                                        !isGroupChat ->
                                                "in:${user.publicKey.trim().lowercase(Locale.ROOT)}"
                                        else -> ""
                                }
                        }
                }
        val isMessageBoundary: (ChatMessage, ChatMessage?) -> Boolean =
                remember(isGroupChat, currentUserPublicKey, user.publicKey) {
                        val maxStackTimeDiffMs =
                                if (isGroupChat) {
                                        GROUP_MESSAGE_STACK_TIME_DIFF_MS
                                } else {
                                        DIRECT_MESSAGE_STACK_TIME_DIFF_MS
                                }
                        { currentMessage, adjacentMessage ->
                                if (adjacentMessage == null) {
                                        true
                                } else if (adjacentMessage.isOutgoing != currentMessage.isOutgoing) {
                                        true
                                } else if (
                                                !isSameLocalDay(
                                                        currentMessage.timestamp.time,
                                                        adjacentMessage.timestamp.time
                                                ) ||
                                                        kotlin.math.abs(
                                                                currentMessage.timestamp.time -
                                                                        adjacentMessage.timestamp.time
                                                        ) > maxStackTimeDiffMs
                                ) {
                                        true
                                } else if (
                                                isGroupChat &&
                                                        !currentMessage.isOutgoing &&
                                                        !adjacentMessage.isOutgoing
                                ) {
                                        val currentSenderIdentity =
                                                resolveSenderIdentity(currentMessage)
                                        val adjacentSenderIdentity =
                                                resolveSenderIdentity(adjacentMessage)
                                        if (
                                                        currentSenderIdentity.isBlank() ||
                                                                adjacentSenderIdentity.isBlank()
                                        ) {
                                                true
                                        } else {
                                                !currentSenderIdentity.equals(
                                                        adjacentSenderIdentity,
                                                        ignoreCase = true
                                                )
                                        }
                                } else {
                                        false
                                }
                        }
                }
        val messageRunNewestIndex =
                remember(messagesWithDates, isGroupChat, currentUserPublicKey, user.publicKey) {
                        IntArray(messagesWithDates.size).also { runHeadByIndex ->
                                messagesWithDates.indices.forEach { messageIndex ->
                                        if (messageIndex == 0) {
                                                runHeadByIndex[messageIndex] = messageIndex
                                        } else {
                                                val currentMessage = messagesWithDates[messageIndex].first
                                                val newerMessage = messagesWithDates[messageIndex - 1].first
                                                runHeadByIndex[messageIndex] =
                                                        if (isMessageBoundary(currentMessage, newerMessage)) {
                                                                messageIndex
                                                        } else {
                                                                runHeadByIndex[messageIndex - 1]
                                                        }
                                        }
                                }
                        }
                }
        val messageRunOldestIndexByHead =
                remember(messageRunNewestIndex) {
                        IntArray(messageRunNewestIndex.size) { it }.also { runTailByHead ->
                                messageRunNewestIndex.indices.forEach { messageIndex ->
                                        val runHeadIndex = messageRunNewestIndex[messageIndex]
                                        runTailByHead[runHeadIndex] = messageIndex
                                }
                        }
                }
        val density = LocalDensity.current
        val incomingRunAvatarSize = 42.dp
        val incomingRunAvatarInsetStart = 6.dp
        val incomingRunAvatarTopGuard = 4.dp
        val incomingRunAvatarUiState by remember(
                isGroupChat,
                messageRunNewestIndex,
                messageRunOldestIndexByHead,
                messagesWithDates,
                listState,
                density
        ) {
                derivedStateOf {
                        if (!isGroupChat || messagesWithDates.isEmpty()) {
                                return@derivedStateOf IncomingRunAvatarUiState(
                                        showOnRunHeads = emptySet(),
                                        showOnRunTails = emptySet(),
                                        overlays = emptyList()
                                )
                        }
                        val layoutInfo = listState.layoutInfo
                        val visibleItems = layoutInfo.visibleItemsInfo
                        if (visibleItems.isEmpty()) {
                                return@derivedStateOf IncomingRunAvatarUiState(
                                        showOnRunHeads = emptySet(),
                                        showOnRunTails = emptySet(),
                                        overlays = emptyList()
                                )
                        }

                        val avatarSizePx = with(density) { incomingRunAvatarSize.toPx() }
                        val topGuardPx = with(density) { incomingRunAvatarTopGuard.toPx() }
                        val viewportStart = layoutInfo.viewportStartOffset.toFloat()
                        val viewportEnd = layoutInfo.viewportEndOffset.toFloat()
                        val maxAvatarTop = viewportEnd - avatarSizePx
                        val visibleIndexSet = visibleItems.map { it.index }.toHashSet()
                        val visibleRuns = linkedMapOf<Int, IncomingRunAvatarAccumulator>()

                        visibleItems.forEach { itemInfo ->
                                val visibleIndex = itemInfo.index
                                if (visibleIndex !in messagesWithDates.indices) {
                                        return@forEach
                                }
                                val message = messagesWithDates[visibleIndex].first
                                if (message.isOutgoing) {
                                        return@forEach
                                }
                                val senderPublicKey = resolveSenderPublicKey(message).trim()
                                if (senderPublicKey.isBlank()) {
                                        return@forEach
                                }
                                val runHeadIndex =
                                        messageRunNewestIndex.getOrNull(visibleIndex)
                                                ?: visibleIndex
                                val itemTopPx = itemInfo.offset.toFloat()
                                val itemBottomPx = (itemInfo.offset + itemInfo.size).toFloat()
                                val currentRun = visibleRuns[runHeadIndex]
                                if (currentRun == null) {
                                        visibleRuns[runHeadIndex] =
                                                IncomingRunAvatarAccumulator(
                                                        senderPublicKey = senderPublicKey,
                                                        senderDisplayName =
                                                                message.senderName.ifBlank {
                                                                        senderPublicKey
                                                                },
                                                        minTopPx = itemTopPx,
                                                        maxBottomPx = itemBottomPx
                                                )
                                } else {
                                        if (itemTopPx < currentRun.minTopPx) {
                                                currentRun.minTopPx = itemTopPx
                                        }
                                        if (itemBottomPx > currentRun.maxBottomPx) {
                                                currentRun.maxBottomPx = itemBottomPx
                                        }
                                }
                        }

                        val showOnRunHeads = hashSetOf<Int>()
                        val showOnRunTails = hashSetOf<Int>()
                        val overlays = arrayListOf<IncomingRunAvatarOverlay>()

                        visibleRuns.forEach { (runHeadIndex, runData) ->
                                val runTailIndex =
                                        messageRunOldestIndexByHead.getOrNull(runHeadIndex)
                                                ?: runHeadIndex
                                val isRunHeadVisible = visibleIndexSet.contains(runHeadIndex)
                                val isRunTailVisible = visibleIndexSet.contains(runTailIndex)

                                when {
                                        isRunHeadVisible -> {
                                                // Start/default phase: keep avatar on the lower (newest) bubble.
                                                showOnRunHeads.add(runHeadIndex)
                                        }
                                        isRunTailVisible -> {
                                                // End phase: keep avatar attached to the last bubble in run.
                                                showOnRunTails.add(runHeadIndex)
                                        }
                                        else -> {
                                                // Middle phase: floating avatar while scrolling through run.
                                                var avatarTopPx =
                                                        kotlin.math.min(
                                                                runData.maxBottomPx - avatarSizePx,
                                                                maxAvatarTop
                                                        )
                                                val topClampPx = runData.minTopPx + topGuardPx
                                                if (avatarTopPx < topClampPx) {
                                                        avatarTopPx = topClampPx
                                                }
                                                if (
                                                        avatarTopPx + avatarSizePx > viewportStart &&
                                                                avatarTopPx < viewportEnd
                                                ) {
                                                        overlays +=
                                                                IncomingRunAvatarOverlay(
                                                                        runHeadIndex = runHeadIndex,
                                                                        senderPublicKey =
                                                                                runData.senderPublicKey,
                                                                        senderDisplayName =
                                                                                runData.senderDisplayName,
                                                                        topPx = avatarTopPx
                                                                )
                                                }
                                        }
                                }
                        }

                        IncomingRunAvatarUiState(
                                showOnRunHeads = showOnRunHeads,
                                showOnRunTails = showOnRunTails,
                                overlays = overlays.sortedBy { it.topPx }
                        )
                }
        }
        val openProfileByPublicKey: (String) -> Unit = { rawPublicKey ->
                val normalizedPublicKey = rawPublicKey.trim()
                if (normalizedPublicKey.isNotBlank()) {
                        scope.launch {
                                val resolvedUser =
                                        messagesViewModel.resolveUserForProfile(normalizedPublicKey)
                                if (resolvedUser != null) {
                                        showContextMenu = false
                                        contextMenuMessage = null
                                        onUserProfileClick(resolvedUser)
                                }
                        }
                }
        }
        // 🔥 Функция для скролла к сообщению с подсветкой
        val scrollToMessage: (String) -> Unit = { messageId ->
                scope.launch {
                        // 🔥 Сбрасываем текущую подсветку перед новым скроллом
                        highlightedMessageId = null
                        delay(50) // Небольшая задержка для сброса анимации

                        // Находим индекс сообщения в списке
                        var messageIndex = messagesWithDates.indexOfFirst { it.first.id == messageId }
                        if (messageIndex == -1) {
                                val loaded = messagesViewModel.ensureMessageLoaded(messageId)
                                if (loaded) {
                                        for (attempt in 0 until 8) {
                                                delay(16)
                                                messageIndex =
                                                        messagesViewModel.messagesWithDates.value.indexOfFirst {
                                                                it.first.id == messageId
                                                        }
                                                if (messageIndex != -1) break
                                        }
                                }
                        }
                        if (messageIndex != -1) {
                                // Скроллим к сообщению
                                listState.animateScrollToItem(messageIndex)

                                // Подсвечиваем на 2 секунды
                                highlightedMessageId = messageId
                                delay(2000)
                                highlightedMessageId = null
                        }
                }
        }
        val closeInChatSearch: () -> Unit = {
                isInChatSearchMode = false
                inChatSearchQuery = ""
                inChatSearchResultIds = emptyList()
                inChatSearchResultIndex = -1
                hideInputOverlays()
        }
        val jumpToSearchResult: (Int) -> Unit = jump@{ targetIndex ->
                val ids = inChatSearchResultIds
                if (ids.isEmpty()) return@jump
                val normalizedIndex =
                        if (targetIndex >= 0) {
                                targetIndex % ids.size
                        } else {
                                ((targetIndex % ids.size) + ids.size) % ids.size
                        }
                inChatSearchResultIndex = normalizedIndex
                scrollToMessage(ids[normalizedIndex])
        }
        val searchResultCounterText =
                remember(inChatSearchResultIds, inChatSearchResultIndex) {
                        if (inChatSearchResultIds.isEmpty() || inChatSearchResultIndex < 0) {
                                "0/0"
                        } else {
                                "${inChatSearchResultIndex + 1}/${inChatSearchResultIds.size}"
                        }
                }

        LaunchedEffect(isInChatSearchMode) {
                if (isInChatSearchMode) {
                        delay(80)
                        searchFieldFocusRequester.requestFocus()
                } else {
                        inChatSearchQuery = ""
                        inChatSearchResultIds = emptyList()
                        inChatSearchResultIndex = -1
                }
        }

        LaunchedEffect(isSelectionMode) {
                if (isSelectionMode && isInChatSearchMode) {
                        closeInChatSearch()
                }
        }

        LaunchedEffect(
                isInChatSearchMode,
                inChatSearchQuery,
                currentUserPublicKey,
                searchDialogKey
        ) {
                if (!isInChatSearchMode) return@LaunchedEffect

                val account = currentUserPublicKey.trim()
                val normalizedQuery = inChatSearchQuery.trim().lowercase(Locale.ROOT)
                if (account.isBlank() || normalizedQuery.length < 2) {
                        inChatSearchResultIds = emptyList()
                        inChatSearchResultIndex = -1
                        return@LaunchedEffect
                }

                delay(250)
                val resultIds =
                        withContext(Dispatchers.IO) {
                                searchIndexDao
                                        .searchInDialog(
                                                account = account,
                                                dialogKey = searchDialogKey,
                                                queryNormalized = normalizedQuery,
                                                limit = 200,
                                                offset = 0
                                        )
                                        .map { it.messageId }
                                        .distinct()
                        }

                inChatSearchResultIds = resultIds
                if (resultIds.isEmpty()) {
                        inChatSearchResultIndex = -1
                } else {
                        inChatSearchResultIndex = 0
                        scrollToMessage(resultIds.first())
                }
        }

        // Динамический subtitle: typing > online > offline
        val isSystemAccount = MessageRepository.isSystemAccount(user.publicKey)
        val groupMembersSubtitleCount = groupMembersCount
        val groupMembersSubtitle =
                if (groupMembersSubtitleCount == null) {
                        ""
                } else {
                        "$groupMembersSubtitleCount member${if (groupMembersSubtitleCount == 1) "" else "s"}"
                }
        val chatSubtitle =
                when {
                        isSavedMessages -> "Notes"
                        isGroupChat -> groupMembersSubtitle
                        isTyping -> "" // Пустая строка, используем компонент TypingIndicator
                        isOnline -> "online"
                        isSystemAccount -> "official account"
                        else -> "offline"
                }

        // 🔥 Обработка системной кнопки назад
        BackHandler {
                when {
                        isSelectionMode -> selectedMessages = emptySet()
                        isInChatSearchMode -> closeInChatSearch()
                        else -> handleBackWithInputPriority()
                }
        }

        // 🔥 Lifecycle-aware отслеживание активности экрана
        val lifecycleOwner = LocalLifecycleOwner.current
        var isScreenActive by remember { mutableStateOf(true) }

        DisposableEffect(lifecycleOwner) {
                val observer = LifecycleEventObserver { _, event ->
                        when (event) {
                                Lifecycle.Event.ON_RESUME -> {
                                        isScreenActive = true
                                        messagesViewModel.setDialogActive(true)
                                        messagesViewModel.markVisibleMessagesAsRead()
                                        // 🔥 Убираем уведомление этого чата из шторки
                                        com.rosetta.messenger.push.RosettaFirebaseMessagingService
                                                .cancelNotificationForChat(context, user.publicKey)
                                }
                                Lifecycle.Event.ON_PAUSE -> {
                                        isScreenActive = false
                                        messagesViewModel.setDialogActive(false)
                                }
                                Lifecycle.Event.ON_STOP -> {
                                        // Hard-stop camera/picker overlays when app goes background.
                                        // On next app open everything must start closed/off.
                                        showInAppCamera = false
                                        showMediaPicker = false
                                        pendingCameraPhotoUri = null
                                        pendingGalleryImages = emptyList()
                                }
                                else -> {}
                        }
                }
                lifecycleOwner.lifecycle.addObserver(observer)
                onDispose {
                        lifecycleOwner.lifecycle.removeObserver(observer)
                        messagesViewModel.closeDialog()
                }
        }

        // 🔥 Подписываемся на forward trigger для перезагрузки при forward в тот же чат
        val forwardTrigger by ForwardManager.forwardTrigger.collectAsState()
        var deferredEmojiPreloadStarted by remember(user.publicKey) { mutableStateOf(false) }

        // Инициализируем ViewModel с ключами и открываем диалог
        // forwardTrigger добавлен чтобы перезагрузить диалог при forward в тот же чат
        LaunchedEffect(
                user.publicKey,
                forwardTrigger,
                currentUserPublicKey,
                currentUserPrivateKey
        ) {
                val normalizedPublicKey = currentUserPublicKey.trim()
                val normalizedPrivateKey = currentUserPrivateKey.trim()
                messagesViewModel.setUserKeys(normalizedPublicKey, normalizedPrivateKey)
                if (normalizedPublicKey.isBlank() || normalizedPrivateKey.isBlank()) {
                        // Fresh registration path can render Chat UI before account keys arrive.
                        // Avoid opening dialog with empty sender/private key.
                        return@LaunchedEffect
                }
                messagesViewModel.openDialog(user.publicKey, user.title, user.username, user.verified)
                messagesViewModel.markVisibleMessagesAsRead()
                // 🔥 Убираем уведомление этого чата из шторки при заходе
                com.rosetta.messenger.push.RosettaFirebaseMessagingService
                        .cancelNotificationForChat(context, user.publicKey)
                // Подписываемся на онлайн статус собеседника
                if (!isSavedMessages && !isGroupChat) {
                        messagesViewModel.subscribeToOnlineStatus()
                }
        }

        // Фиксируем момент, когда список впервые реально отрисовался.
        LaunchedEffect(listState, user.publicKey) {
                snapshotFlow {
                        val hasVisibleItems = listState.layoutInfo.visibleItemsInfo.isNotEmpty()
                        hasVisibleItems && messagesWithDates.isNotEmpty()
                }
                        .distinctUntilChanged()
                        .collect { isReady ->
                                if (isReady) {
                                        messagesViewModel.markFirstListLayoutReady()
                                }
                        }
        }

        // Отложенный preload эмодзи после первого layout списка, чтобы не мешать открытию диалога.
        LaunchedEffect(user.publicKey, chatOpenMetrics.firstListLayoutMs) {
                if (!deferredEmojiPreloadStarted && chatOpenMetrics.firstListLayoutMs != null) {
                        deferredEmojiPreloadStarted = true
                        delay(300)
                        messagesViewModel.addChatOpenTraceEvent("deferred_emoji_preload_start")
                        withContext(Dispatchers.Default) {
                                com.rosetta.messenger.ui.components.EmojiCache.preload(context)
                        }
                }
        }

        // Consume pending forward messages for this chat
        LaunchedEffect(user.publicKey, forwardTrigger) {
                val pendingForwards = ForwardManager.consumeForwardMessagesForChat(user.publicKey)
                if (pendingForwards.isNotEmpty()) {
                        messagesViewModel.sendForwardDirectly(user.publicKey, pendingForwards)
                }
        }

        // Отмечаем сообщения как прочитанные только когда экран активен (RESUMED)
        LaunchedEffect(messages, isScreenActive) {
                if (messages.isNotEmpty() && isScreenActive) {
                        messagesViewModel.markVisibleMessagesAsRead()
                }
        }

        // 🔥 Отслеживаем ID самого нового сообщения для умного скролла
        // ВАЖНО: Используем maxByOrNull по timestamp, НЕ firstOrNull()!
        // При пагинации старые сообщения добавляются в начало списка,
        // поэтому firstOrNull() возвращает старое сообщение, а не новое.
        val newestMessageId =
                messages
                        .maxWithOrNull(compareBy<ChatMessage>({ it.timestamp.time }, { it.id }))
                        ?.id
        var lastNewestMessageId by remember { mutableStateOf<String?>(null) }
        val isAtBottom by remember(listState) {
                derivedStateOf {
                        listState.firstVisibleItemIndex == 0 &&
                                listState.firstVisibleItemScrollOffset <= 12
                }
        }
        val showScrollToBottomButton by remember(messagesWithDates, isAtBottom, isSendingMessage) {
                derivedStateOf { messagesWithDates.isNotEmpty() && !isAtBottom && !isSendingMessage }
        }
        val floatingDateText by remember(messagesWithDates, listState) {
                derivedStateOf {
                        if (messagesWithDates.isEmpty()) {
                                return@derivedStateOf null
                        }
                        val layoutInfo = listState.layoutInfo
                        val visibleItems = layoutInfo.visibleItemsInfo
                        if (visibleItems.isEmpty()) {
                                return@derivedStateOf null
                        }
                        val topVisibleItem =
                                visibleItems.minByOrNull { itemInfo ->
                                        kotlin.math.abs(itemInfo.offset - layoutInfo.viewportStartOffset)
                                } ?: return@derivedStateOf null
                        val messageIndex = topVisibleItem.index
                        if (messageIndex !in messagesWithDates.indices) {
                                return@derivedStateOf null
                        }
                        getDateText(messagesWithDates[messageIndex].first.timestamp.time)
                }
        }
        val showFloatingDateHeader by
                remember(messagesWithDates, floatingDateText, isAtBottom, listState) {
                        derivedStateOf {
                                messagesWithDates.isNotEmpty() &&
                                        floatingDateText != null &&
                                        !isAtBottom &&
                                        listState.isScrollInProgress
                        }
                }

        // Telegram-style: Прокрутка ТОЛЬКО при новых сообщениях (не при пагинации)
        // 🔥 Скроллим только если изменился ID самого нового сообщения
        // При пагинации добавляются старые сообщения - ID нового не меняется
        LaunchedEffect(newestMessageId) {
                if (newestMessageId != null &&
                                lastNewestMessageId != null &&
                                newestMessageId != lastNewestMessageId
                ) {
                        val newestMessage = messages.firstOrNull { it.id == newestMessageId }
                        val isOwnOutgoingMessage = newestMessage?.isOutgoing == true
                        val shouldAutoScroll = isAtBottom || isOwnOutgoingMessage

                        if (shouldAutoScroll) {
                                delay(50) // Debounce - ждём стабилизации
                                listState.animateScrollToItem(0)
                                wasManualScroll = false
                        }
                }
                lastNewestMessageId = newestMessageId
        }

        // Аватар - используем publicKey для консистентности цвета везде
        @Suppress("UNUSED_VARIABLE")
        val avatarColors =
                getAvatarColor(
                        if (isSavedMessages) "SavedMessages" else user.publicKey,
                        isDarkTheme
                )

        // Smooth transition when switching to another dialog from in-message mentions/tags.
        // SwipeBackContainer stays mounted, so we animate content change locally.
        var runDialogSwitchEnterAnimation by remember(user.publicKey) { mutableStateOf(false) }
        LaunchedEffect(user.publicKey) {
                runDialogSwitchEnterAnimation = false
                withFrameNanos { }
                runDialogSwitchEnterAnimation = true
        }
        val dialogSwitchAlpha by
                animateFloatAsState(
                        targetValue = if (runDialogSwitchEnterAnimation) 1f else 0.9f,
                        animationSpec = tween(durationMillis = 240),
                        label = "dialogSwitchAlpha"
                )
        val dialogSwitchOffsetX by
                animateDpAsState(
                        targetValue = if (runDialogSwitchEnterAnimation) 0.dp else 24.dp,
                        animationSpec = tween(durationMillis = 240),
                        label = "dialogSwitchOffsetX"
                )

        // 🚀 Весь контент (swipe-back обрабатывается в SwipeBackContainer)
        Box(
                modifier =
                        Modifier.fillMaxSize().graphicsLayer {
                                alpha = dialogSwitchAlpha
                                translationX = with(density) { dialogSwitchOffsetX.toPx() }
                        }
        ) {
                // Telegram-style solid header background (без blur)
                val headerBackground = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFF228BE6)

                Scaffold(
                        contentWindowInsets = WindowInsets(0.dp),
                        topBar = {
                            Column {
                                // 🔥 UNIFIED HEADER - один контейнер, контент меняется внутри
                                Box(
                                        modifier =
                                                Modifier.fillMaxWidth()
                                                        .background(headerBackground)
                                                        // 🎨 statusBarsPadding ПОСЛЕ background =
                                                        // хедер начинается под статус баром
                                                        .statusBarsPadding()
                                ) {
                                        // Контент хедера с Crossfade для плавной смены - ускоренная
                                        // анимация
                                        val headerMode =
                                                when {
                                                        isSelectionMode -> ChatHeaderMode.SELECTION
                                                        isInChatSearchMode -> ChatHeaderMode.SEARCH
                                                        else -> ChatHeaderMode.NORMAL
                                                }
                                        Crossfade(
                                                targetState = headerMode,
                                                animationSpec = tween(150),
                                                label = "headerContent"
                                        ) { currentHeaderMode ->
                                                if (currentHeaderMode == ChatHeaderMode.SELECTION) {
                                                        val selectedPinMessageId =
                                                                remember(selectedMessages, messages) {
                                                                        if (selectedMessages.size != 1) {
                                                                                null
                                                                        } else {
                                                                                messages
                                                                                        .firstOrNull {
                                                                                                selectedMessages.contains(it.id) &&
                                                                                                        it.attachments.none { attachment ->
                                                                                                                attachment.type == AttachmentType.AVATAR
                                                                                                        }
                                                                                        }
                                                                                        ?.id
                                                                        }
                                                                }
                                                        val selectedPinMessageIsPinned =
                                                                remember(selectedPinMessageId, pinnedMessages) {
                                                                        selectedPinMessageId != null &&
                                                                                pinnedMessages.any { it.messageId == selectedPinMessageId }
                                                                }
                                                        val canToggleSelectedPin = selectedPinMessageId != null
                                                        // SELECTION MODE CONTENT
                                                        Row(
                                                                modifier =
                                                                        Modifier.fillMaxWidth()
                                                                                .height(64.dp)
                                                                                .padding(
                                                                                        horizontal =
                                                                                                4.dp
                                                                                ),
                                                                verticalAlignment =
                                                                        Alignment.CenterVertically,
                                                                horizontalArrangement =
                                                                        Arrangement.SpaceBetween
                                                        ) {
                                                                // Left: X (cancel) + Count
                                                                Row(
                                                                        verticalAlignment =
                                                                                Alignment
                                                                                        .CenterVertically
                                                                ) {
                                                                        IconButton(
                                                                                onClick = {
                                                                                        selectedMessages =
                                                                                                emptySet()
                                                                                }
                                                                        ) {
                                                                                Icon(
                                                                                        painter = TelegramIcons.Close,
                                                                                        contentDescription =
                                                                                                "Cancel",
                                                                                        tint = headerIconColor,
                                                                                        modifier =
                                                                                                Modifier.size(
                                                                                                        24.dp
                                                                                                )
                                                                                )
                                                                        }
                                                                        Spacer(
                                                                                modifier =
                                                                                        Modifier.width(
                                                                                                8.dp
                                                                                        )
                                                                        )
                                                                        Text(
                                                                                "${selectedMessages.size}",
                                                                                fontSize = 20.sp,
                                                                                fontWeight =
                                                                                        FontWeight
                                                                                                .Bold,
                                                                                color = Color.White
                                                                        )
                                                                }

                                                                // Right: Action buttons
                                                                Row(
                                                                        horizontalArrangement =
                                                                                Arrangement
                                                                                        .spacedBy(
                                                                                                4.dp
                                                                                        ),
                                                                        verticalAlignment =
                                                                                Alignment
                                                                                        .CenterVertically
                                                                ) {
                                                                        // Copy button
                                                                        IconButton(
                                                                                onClick = {
                                                                                        val textToCopy =
                                                                                                messages
                                                                                                        .filter {
                                                                                                                selectedMessages
                                                                                                                        .contains(it.id) &&
                                                                                                                        it.attachments.none { attachment ->
                                                                                                                                attachment.type == AttachmentType.AVATAR
                                                                                                                        }
                                                                                                        }
                                                                                                        .sortedWith(
                                                                                                                compareBy<ChatMessage>(
                                                                                                                        { it.timestamp.time },
                                                                                                                        { it.id }
                                                                                                                )
                                                                                                        )
                                                                                                        .mapNotNull {
                                                                                                                msg
                                                                                                                ->
                                                                                                                val messageText =
                                                                                                                        extractCopyableMessageText(
                                                                                                                                msg
                                                                                                                        )
                                                                                                                messageText
                                                                                                                        .takeIf {
                                                                                                                                it.isNotBlank()
                                                                                                                        }
                                                                                                        }
                                                                                                        .joinToString(
                                                                                                                "\n\n"
                                                                                                        )
                                                                                        if (textToCopy.isNotBlank()) {
                                                                                                clipboardManager
                                                                                                        .setText(
                                                                                                                androidx.compose
                                                                                                                        .ui
                                                                                                                        .text
                                                                                                                        .AnnotatedString(
                                                                                                                                textToCopy
                                                                                                                        )
                                                                                                        )
                                                                                        }
                                                                                        selectedMessages =
                                                                                                emptySet()
                                                                                }
                                                                        ) {
                                                                                Icon(
                                                                                        Icons.Default
                                                                                                .ContentCopy,
                                                                                        contentDescription =
                                                                                                "Copy",
                                                                                        tint = headerIconColor,
                                                                                        modifier =
                                                                                                Modifier.size(
                                                                                                        22.dp
                                                                                                )
                                                                                )
                                                                        }

                                                                        // Pin / Unpin button
                                                                        IconButton(
                                                                                onClick = {
                                                                                        val targetId =
                                                                                                selectedPinMessageId
                                                                                                        ?: return@IconButton
                                                                                        if (selectedPinMessageIsPinned) {
                                                                                                messagesViewModel.unpinMessage(targetId)
                                                                                        } else {
                                                                                                messagesViewModel.pinMessage(targetId)
                                                                                                isPinnedBannerDismissed =
                                                                                                        false
                                                                                        }
                                                                                        selectedMessages =
                                                                                                emptySet()
                                                                                },
                                                                                enabled =
                                                                                        canToggleSelectedPin
                                                                        ) {
                                                                                Icon(
                                                                                        painter =
                                                                                                if (selectedPinMessageIsPinned) {
                                                                                                        TelegramIcons.Unpin
                                                                                                } else {
                                                                                                        TelegramIcons.Pin
                                                                                                },
                                                                                        contentDescription =
                                                                                                if (selectedPinMessageIsPinned) {
                                                                                                        "Unpin"
                                                                                                } else {
                                                                                                        "Pin"
                                                                                                },
                                                                                        tint =
                                                                                                if (canToggleSelectedPin) {
                                                                                                        headerIconColor
                                                                                                } else {
                                                                                                        headerIconColor.copy(
                                                                                                                alpha =
                                                                                                                        0.45f
                                                                                                        )
                                                                                                },
                                                                                        modifier =
                                                                                                Modifier.size(
                                                                                                        22.dp
                                                                                                )
                                                                                )
                                                                        }

                                                                        // Delete button
                                                                        IconButton(
                                                                                onClick = {
                                                                                        messages
                                                                                                .filter {
                                                                                                        selectedMessages
                                                                                                                .contains(it.id) &&
                                                                                                                it.attachments.none { attachment ->
                                                                                                                        attachment.type == AttachmentType.AVATAR
                                                                                                                }
                                                                                                }
                                                                                                .forEach {
                                                                                                        msg
                                                                                                        ->
                                                                                                        messagesViewModel
                                                                                                                .deleteMessage(msg.id)
                                                                                                }
                                                                                        selectedMessages =
                                                                                                emptySet()
                                                                                }
                                                                        ) {
                                                                                Icon(
                                                                                        Icons.Default
                                                                                                .Delete,
                                                                                        contentDescription =
                                                                                                "Delete",
                                                                                        tint = headerIconColor,
                                                                                        modifier =
                                                                                                Modifier.size(
                                                                                                        22.dp
                                                                                                )
                                                                                )
                                                                        }
                                                                }
                                                        }
                                                } else if (currentHeaderMode == ChatHeaderMode.SEARCH) {
                                                        // SEARCH MODE CONTENT
                                                        Row(
                                                                modifier =
                                                                        Modifier.fillMaxWidth()
                                                                                .height(64.dp)
                                                                                .padding(horizontal = 4.dp),
                                                                verticalAlignment = Alignment.CenterVertically
                                                        ) {
                                                                IconButton(
                                                                        onClick = { closeInChatSearch() },
                                                                        modifier = Modifier.size(40.dp)
                                                                ) {
                                                                        Icon(
                                                                                imageVector = TablerIcons.ChevronLeft,
                                                                                contentDescription = "Close search",
                                                                                tint = Color.White,
                                                                                modifier = Modifier.size(28.dp)
                                                                        )
                                                                }

                                                                TextField(
                                                                        value = inChatSearchQuery,
                                                                        onValueChange = { value ->
                                                                                inChatSearchQuery = value
                                                                        },
                                                                        singleLine = true,
                                                                        modifier =
                                                                                Modifier.weight(1f)
                                                                                        .focusRequester(searchFieldFocusRequester),
                                                                        textStyle =
                                                                                LocalTextStyle.current.copy(
                                                                                        color = Color.White,
                                                                                        fontSize = 17.sp,
                                                                                        fontWeight = FontWeight.Medium
                                                                                ),
                                                                        placeholder = {
                                                                                Text(
                                                                                        text = "Search in chat",
                                                                                        color = Color.White.copy(alpha = 0.65f),
                                                                                        fontSize = 16.sp
                                                                                )
                                                                        },
                                                                        leadingIcon = {
                                                                                Icon(
                                                                                        imageVector = Icons.Default.Search,
                                                                                        contentDescription = null,
                                                                                        tint = Color.White.copy(alpha = 0.75f)
                                                                                )
                                                                        },
                                                                        trailingIcon = {
                                                                                if (inChatSearchQuery.isNotBlank()) {
                                                                                        IconButton(
                                                                                                onClick = {
                                                                                                        inChatSearchQuery = ""
                                                                                                        inChatSearchResultIds = emptyList()
                                                                                                        inChatSearchResultIndex = -1
                                                                                                }
                                                                                        ) {
                                                                                                Icon(
                                                                                                        imageVector = Icons.Default.Close,
                                                                                                        contentDescription = "Clear search",
                                                                                                        tint = Color.White.copy(alpha = 0.85f)
                                                                                                )
                                                                                        }
                                                                                }
                                                                        },
                                                                        colors =
                                                                                TextFieldDefaults.colors(
                                                                                        focusedContainerColor = Color.Transparent,
                                                                                        unfocusedContainerColor = Color.Transparent,
                                                                                        disabledContainerColor = Color.Transparent,
                                                                                        focusedIndicatorColor = Color.Transparent,
                                                                                        unfocusedIndicatorColor = Color.Transparent,
                                                                                        disabledIndicatorColor = Color.Transparent,
                                                                                        focusedTextColor = Color.White,
                                                                                        unfocusedTextColor = Color.White,
                                                                                        cursorColor = Color.White
                                                                                )
                                                                )

                                                                Text(
                                                                        text = searchResultCounterText,
                                                                        color = Color.White.copy(alpha = 0.78f),
                                                                        fontSize = 12.sp,
                                                                        fontWeight = FontWeight.Medium,
                                                                        modifier = Modifier.padding(horizontal = 4.dp)
                                                                )

                                                                IconButton(
                                                                        onClick = {
                                                                                jumpToSearchResult(
                                                                                        inChatSearchResultIndex - 1
                                                                                )
                                                                        },
                                                                        enabled = inChatSearchResultIds.isNotEmpty()
                                                                ) {
                                                                        Icon(
                                                                                imageVector = TablerIcons.ChevronUp,
                                                                                contentDescription = "Previous result",
                                                                                tint =
                                                                                        if (inChatSearchResultIds.isNotEmpty()) Color.White
                                                                                        else Color.White.copy(alpha = 0.45f)
                                                                        )
                                                                }

                                                                IconButton(
                                                                        onClick = {
                                                                                jumpToSearchResult(
                                                                                        inChatSearchResultIndex + 1
                                                                                )
                                                                        },
                                                                        enabled = inChatSearchResultIds.isNotEmpty()
                                                                ) {
                                                                        Icon(
                                                                                imageVector = TablerIcons.ChevronDown,
                                                                                contentDescription = "Next result",
                                                                                tint =
                                                                                        if (inChatSearchResultIds.isNotEmpty()) Color.White
                                                                                        else Color.White.copy(alpha = 0.45f)
                                                                        )
                                                                }
                                                        }
                                                } else {
                                                        // NORMAL HEADER CONTENT
                                                        Row(
                                                                modifier =
                                                                        Modifier.fillMaxWidth()
                                                                                .height(64.dp)
                                                                                .padding(
                                                                                        horizontal =
                                                                                                4.dp
                                                                                ),
                                                                verticalAlignment =
                                                                        Alignment.CenterVertically
                                                        ) {
                                                                // Back button with badge
                                                                Box {
                                                                        IconButton(
                                                                                onClick =
                                                                                        handleBackWithInputPriority,
                                                                                modifier =
                                                                                        Modifier.size(
                                                                                                40.dp
                                                                                        )
                                                                        ) {
                                                                                Icon(
                                                                                        imageVector = TablerIcons.ChevronLeft,
                                                                                        contentDescription =
                                                                                                "Back",
                                                                                        tint = Color.White,
                                                                                        modifier =
                                                                                                Modifier.size(
                                                                                                        28.dp
                                                                                                )
                                                                                )
                                                                        }
                                                                        if (totalUnreadFromOthers >
                                                                                        0
                                                                        ) {
                                                                                Box(
                                                                                        modifier =
                                                                                                Modifier.align(
                                                                                                                Alignment
                                                                                                                        .TopEnd
                                                                                                        )
                                                                                                        .offset(
                                                                                                                x =
                                                                                                                        2.dp,
                                                                                                                y =
                                                                                                                        2.dp
                                                                                                        )
                                                                                                        .size(
                                                                                                                if (totalUnreadFromOthers >
                                                                                                                                9
                                                                                                                )
                                                                                                                        20.dp
                                                                                                                else
                                                                                                                        18.dp
                                                                                                        )
                                                                                                        .clip(
                                                                                                                CircleShape
                                                                                                        )
                                                                                                        .background(
                                                                                                                Color(
                                                                                                                        0xFFFF3B30
                                                                                                                )
                                                                                                        ),
                                                                                        contentAlignment =
                                                                                                Alignment
                                                                                                        .Center
                                                                                ) {
                                                                                        Text(
                                                                                                text =
                                                                                                        if (totalUnreadFromOthers >
                                                                                                                        99
                                                                                                        )
                                                                                                                "99+"
                                                                                                        else
                                                                                                                "$totalUnreadFromOthers",
                                                                                                color =
                                                                                                        Color.White,
                                                                                                fontSize =
                                                                                                        if (totalUnreadFromOthers >
                                                                                                                        9
                                                                                                        )
                                                                                                                9.sp
                                                                                                        else
                                                                                                                10.sp,
                                                                                                fontWeight =
                                                                                                        FontWeight
                                                                                                                .Bold,
                                                                                                maxLines =
                                                                                                        1
                                                                                        )
                                                                                }
                                                                        }
                                                                }

                                                                Spacer(
                                                                        modifier =
                                                                                Modifier.width(4.dp)
                                                                )

                                                                // Аватар
                                                                Box(
                                                                        modifier =
                                                                                Modifier.size(40.dp)
                                                                                        .then(
                                                                                                if (!isSavedMessages) {
                                                                                                        Modifier
                                                                                                                .clickable(
                                                                                                                        indication =
                                                                                                                                null,
                                                                                                                        interactionSource =
                                                                                                                                remember {
                                                                                                                                        MutableInteractionSource()
                                                                                                                                }
                                                                                                                ) {
                                                                                                                        openDialogInfo()
                                                                                                                }
                                                                                                } else
                                                                                                        Modifier
                                                                                        ),
                                                                        contentAlignment =
                                                                                Alignment.Center
                                                                ) {
                                                                        if (isSavedMessages) {
                                                                                Box(
                                                                                        modifier =
                                                                                                Modifier.fillMaxSize()
                                                                                                        .clip(
                                                                                                                CircleShape
                                                                                                        )
                                                                                                        .background(
                                                                                                                Color(0xFF228BE6)
                                                                                                        ),
                                                                                        contentAlignment =
                                                                                                Alignment
                                                                                                        .Center
                                                                                ) {
                                                                                        Icon(
                                                                                                painter = painterResource(R.drawable.bookmark_outlined),
                                                                                                contentDescription =
                                                                                                        null,
                                                                                                tint =
                                                                                                        Color.White,
                                                                                                modifier =
                                                                                                        Modifier.size(
                                                                                                                20.dp
                                                                                                        )
                                                                                        )
                                                                                }
                                                                        } else {
                                                                                AvatarImage(
                                                                                        publicKey =
                                                                                                user.publicKey,
                                                                                        avatarRepository =
                                                                                                avatarRepository,
                                                                                        size =
                                                                                                40.dp,
                                                                                        isDarkTheme =
                                                                                                isDarkTheme,
                                                                                        displayName =
                                                                                                user.title
                                                                                                        .ifEmpty {
                                                                                                                user.username
                                                                                                        } // 🔥 Для инициалов
                                                                                )
                                                                        }
                                                                }

                                                                Spacer(
                                                                        modifier =
                                                                                Modifier.width(
                                                                                        12.dp
                                                                                )
                                                                )

                                                                // Информация о пользователе
                                                                Column(
                                                                        modifier =
                                                                                Modifier.weight(1f)
                                                                                        .then(
                                                                                                if (!isSavedMessages) {
                                                                                                        Modifier
                                                                                                                .clickable(
                                                                                                                        indication =
                                                                                                                                null,
                                                                                                                        interactionSource =
                                                                                                                                remember {
                                                                                                                                        MutableInteractionSource()
                                                                                                                                }
                                                                                                                ) {
                                                                                                                        openDialogInfo()
                                                                                                                }
                                                                                                } else
                                                                                                        Modifier
                                                                                        )
                                                                ) {
                                                                        Row(
                                                                                verticalAlignment =
                                                                                        Alignment
                                                                                                .CenterVertically
                                                                        ) {
                                                                                AppleEmojiText(
                                                                                        text = chatTitle,
                                                                                        fontSize = 16.sp,
                                                                                        fontWeight = FontWeight.SemiBold,
                                                                                        color = Color.White,
                                                                                        maxLines = 1,
                                                                                        overflow = android.text.TextUtils.TruncateAt.END,
                                                                                        enableLinks = false,
                                                                                        minHeightMultiplier = 1.1f
                                                                                )
                                                                                if (!isSavedMessages &&
                                                                                                !isGroupChat &&
                                                                                                (chatHeaderVerified >
                                                                                                        0 || isSystemAccount)
                                                                                ) {
                                                                                        Spacer(
                                                                                                modifier =
                                                                                                        Modifier.width(
                                                                                                                4.dp
                                                                                                        )
                                                                                        )
                                                                                        VerifiedBadge(
                                                                                                verified =
                                                                                                        if (chatHeaderVerified > 0) chatHeaderVerified else 1,
                                                                                                size =
                                                                                                        16,
                                                                                                isDarkTheme =
                                                                                                        isDarkTheme,
                                                                                                badgeTint = if (isDarkTheme) null else Color(0xFFACD2F9)
                                                                                        )
                                                                                }
                                                                                // 🔇 Mute icon
                                                                                if (isChatMuted.value) {
                                                                                        Spacer(modifier = Modifier.width(4.dp))
                                                                                        Icon(
                                                                                                painter = TelegramIcons.Mute,
                                                                                                contentDescription = "Muted",
                                                                                                modifier = Modifier.size(16.dp),
                                                                                                tint = Color.White.copy(alpha = 0.7f)
                                                                                        )
                                                                                }
                                                                        }
                                                                        // Typing indicator или
                                                                        // subtitle
                                                                        if (isTyping) {
                                                                                TypingIndicator(
                                                                                        isDarkTheme =
                                                                                                isDarkTheme,
                                                                                        typingDisplayName =
                                                                                                if (isGroupChat) typingDisplayName else "",
                                                                                        typingSenderPublicKey =
                                                                                                if (isGroupChat) typingDisplayPublicKey else ""
                                                                                )
                                                                        } else if (isGroupChat &&
                                                                                groupMembersCount == null
                                                                        ) {
                                                                                GroupMembersSubtitleSkeleton()
                                                                        } else {
                                                                                Text(
                                                                                        text =
                                                                                                chatSubtitle,
                                                                                        fontSize =
                                                                                                13.sp,
                                                                                        color =
                                                                                                when {
                                                                                                        isSavedMessages ->
                                                                                                                Color.White.copy(alpha = 0.7f)
                                                                                                        isOnline ->
                                                                                                                if (isDarkTheme) Color(0xFF7FE084)
                                                                                                                else Color.White.copy(alpha = 0.7f)
                                                                                                        else ->
                                                                                                                Color.White.copy(alpha = 0.7f)
                                                                                                },
                                                                                        maxLines = 1
                                                                                )
                                                                        }
                                                                }
                                                                // Кнопки действий
                                                                if (!isSavedMessages &&
                                                                                !isGroupChat &&
                                                                                !isSystemAccount
                                                                ) {
                                                                        IconButton(
                                                                                onClick = { onCallClick(user) }
                                                                        ) {
                                                                                Icon(
                                                                                        Icons.Default
                                                                                                .Call,
                                                                                        contentDescription =
                                                                                                "Call",
                                                                                        tint = Color.White
                                                                                )
                                                                        }
                                                                }

                                                                // Кнопка меню - открывает kebab
                                                                // menu
                                                                Box {
                                                                        IconButton(
                                                                                onClick = {
                                                                                        // Закрываем клавиатуру/emoji перед открытием меню
                                                                                        hideInputOverlays()
                                                                                        showMenu =
                                                                                                true
                                                                                },
                                                                                modifier =
                                                                                        Modifier.size(
                                                                                                        48.dp
                                                                                                )
                                                                                                .clip(
                                                                                                        CircleShape
                                                                                                )
                                                                        ) {
                                                                                Icon(
                                                                                        Icons.Default
                                                                                                .MoreVert,
                                                                                        contentDescription =
                                                                                                "More",
                                                                                        tint = Color.White,
                                                                                        modifier =
                                                                                                Modifier.size(
                                                                                                        26.dp
                                                                                                )
                                                                                )
                                                                        }

                                                                        // 🔥 TELEGRAM-STYLE KEBAB
                                                                        // MENU
                                                                        KebabMenu(
                                                                                expanded = showMenu,
                                                                                onDismiss = {
                                                                                        showMenu =
                                                                                                false
                                                                                },
                                                                                isDarkTheme =
                                                                                        isDarkTheme,
                                                                                isSavedMessages =
                                                                                        isSavedMessages,
                                                                                isGroupChat =
                                                                                        isGroupChat,
                                                                                isSystemAccount =
                                                                                        isSystemAccount,
                                                                                isBlocked =
                                                                                        isBlocked,
                                                                                onChatOpenMetricsClick = {
                                                                                        showMenu = false
                                                                                        showChatOpenMetricsDialog = true
                                                                                },
                                                                                onSearchInChatClick = {
                                                                                        showMenu = false
                                                                                        hideInputOverlays()
                                                                                        isInChatSearchMode = true
                                                                                        inChatSearchQuery = ""
                                                                                        inChatSearchResultIds = emptyList()
                                                                                        inChatSearchResultIndex = -1
                                                                                },
                                                                                onGroupInfoClick = {
                                                                                        showMenu =
                                                                                                false
                                                                                        hideInputOverlays()
                                                                                        onGroupInfoClick(
                                                                                                user
                                                                                        )
                                                                                },
                                                                                onSearchMembersClick = {
                                                                                        showMenu =
                                                                                                false
                                                                                        hideInputOverlays()
                                                                                        onGroupInfoClick(
                                                                                                user
                                                                                        )
                                                                                },
                                                                                onLeaveGroupClick = {
                                                                                        showMenu =
                                                                                                false
                                                                                        showDeleteConfirm =
                                                                                                true
                                                                                },
                                                                                onBlockClick = {
                                                                                        showMenu =
                                                                                                false
                                                                                        showBlockConfirm =
                                                                                                true
                                                                                },
                                                                                onUnblockClick = {
                                                                                        showMenu =
                                                                                                false
                                                                                        showUnblockConfirm =
                                                                                                true
                                                                                },
                                                                                onDeleteClick = {
                                                                                        showMenu =
                                                                                                false
                                                                                        showDeleteConfirm =
                                                                                                true
                                                                                }
                                                                        )
                                                                }
                                                        }
                                                }
                                        } // Закрытие Crossfade

                                        // Bottom line для unified header
                                        val headerDividerColor =
                                                when {
                                                        showMediaPicker -> Color.Transparent
                                                        isDarkTheme -> Color.Black.copy(alpha = 0.26f)
                                                        else -> Color.White.copy(alpha = 0.15f)
                                                }
                                        Box(
                                                modifier =
                                                        Modifier.align(Alignment.BottomCenter)
                                                                .fillMaxWidth()
                                                                .height(0.5.dp)
                                                                .background(headerDividerColor)
                                        )
                                } // Закрытие Box unified header

                                // 📌 PINNED MESSAGE BANNER with shrink animation
                                androidx.compose.animation.AnimatedVisibility(
                                    visible = pinnedMessages.isNotEmpty() && !isPinnedBannerDismissed,
                                    enter = expandVertically(
                                            animationSpec = tween(250, easing = androidx.compose.animation.core.FastOutSlowInEasing)
                                    ) + fadeIn(tween(200)),
                                    exit = shrinkVertically(
                                            animationSpec = tween(250, easing = androidx.compose.animation.core.FastOutSlowInEasing)
                                    ) + fadeOut(tween(150))
                                ) {
                                    val idx = currentPinnedIndex.coerceIn(0, (pinnedMessages.size - 1).coerceAtLeast(0))
                                    PinnedMessageBanner(
                                            pinnedCount = pinnedMessages.size.coerceAtLeast(1),
                                            currentIndex = idx,
                                            messagePreview = currentPinnedMessagePreview,
                                            isDarkTheme = isDarkTheme,
                                            onBannerClick = {
                                                if (pinnedMessages.isNotEmpty()) {
                                                    val messageId = messagesViewModel.navigateToNextPinned()
                                                    if (messageId != null) {
                                                        scrollToMessage(messageId)
                                                    }
                                                }
                                            },
                                            onCloseClick = {
                                                if (pinnedMessages.isNotEmpty()) {
                                                    // 📌 Открепляем текущий показанный пин
                                                    val pinIdx = currentPinnedIndex.coerceIn(0, pinnedMessages.size - 1)
                                                    val pinToRemove = pinnedMessages[pinIdx]
                                                    messagesViewModel.unpinMessage(pinToRemove.messageId)
                                                }
                                            }
                                    )
                                }
                                // Баннер активного звонка (как в чат-листе)
                                val callUiState by CallManager.state.collectAsState()
                                val showCallBanner = callUiState.isVisible &&
                                    callUiState.phase != CallPhase.INCOMING
                                if (showCallBanner) {
                                    CallTopBanner(
                                        state = callUiState,
                                        onOpenCall = onOpenCallOverlay,
                                        isSticky = true,
                                        isDarkTheme = isDarkTheme,
                                        avatarRepository = avatarRepository
                                    )
                                }
                                // Voice mini player — shown right under the chat header when audio is playing
                                val playingVoiceAttachmentId by VoicePlaybackCoordinator.playingAttachmentId.collectAsState()
                                val isVoicePlaybackRunning by VoicePlaybackCoordinator.isPlaying.collectAsState()
                                val voicePlaybackSpeed by VoicePlaybackCoordinator.playbackSpeed.collectAsState()
                                val playingVoiceSenderLabel by VoicePlaybackCoordinator.playingSenderLabel.collectAsState()
                                val playingVoiceTimeLabel by VoicePlaybackCoordinator.playingTimeLabel.collectAsState()
                                AnimatedVisibility(
                                    visible = !playingVoiceAttachmentId.isNullOrBlank(),
                                    enter = expandVertically(
                                        animationSpec = tween(220, easing = androidx.compose.animation.core.FastOutSlowInEasing),
                                        expandFrom = Alignment.Top
                                    ) + fadeIn(animationSpec = tween(220)),
                                    exit = shrinkVertically(
                                        animationSpec = tween(260, easing = androidx.compose.animation.core.FastOutSlowInEasing),
                                        shrinkTowards = Alignment.Top
                                    ) + fadeOut(animationSpec = tween(180))
                                ) {
                                    val sender = playingVoiceSenderLabel.trim().ifBlank { "Voice" }
                                    val time = playingVoiceTimeLabel.trim()
                                    val voiceTitle = if (time.isBlank()) sender else "$sender at $time"
                                    VoiceTopMiniPlayer(
                                        title = voiceTitle,
                                        isDarkTheme = isDarkTheme,
                                        isPlaying = isVoicePlaybackRunning,
                                        speed = voicePlaybackSpeed,
                                        onTogglePlay = { VoicePlaybackCoordinator.toggleCurrentPlayback() },
                                        onCycleSpeed = { VoicePlaybackCoordinator.cycleSpeed() },
                                        onClose = { VoicePlaybackCoordinator.stop() }
                                    )
                                }
                            } // Закрытие Column topBar
                        },
                        containerColor = backgroundColor, // Фон всего чата
                        // 🔥 Bottom bar - инпут с умным padding
                        bottomBar = {
                                val useImePadding =
                                        !coordinator.isEmojiBoxVisible
                                val bottomModifier =
                                        if (useImePadding) {
                                                Modifier.imePadding()
                                        } else {
                                                Modifier
                                        }

                                Column(modifier = bottomModifier) {
                                        // 🔥 UNIFIED BOTTOM BAR
                                        AnimatedContent(
                                                targetState = isSelectionMode,
                                                transitionSpec = {
                                                        (slideInVertically(
                                                                initialOffsetY = { fullHeight -> fullHeight },
                                                                animationSpec = tween(250)
                                                        ) + fadeIn(
                                                                animationSpec = tween(200)
                                                        )) togetherWith (
                                                                slideOutVertically(
                                                                        targetOffsetY = { fullHeight -> fullHeight },
                                                                        animationSpec = tween(200)
                                                                ) + fadeOut(
                                                                        animationSpec = tween(150)
                                                                )
                                                        ) using SizeTransform(
                                                                clip = true,
                                                                sizeAnimationSpec = { _, _ -> tween(250) }
                                                        )
                                                },
                                                label = "bottomBarContent"
                                        ) { selectionMode ->
                                                if (selectionMode) {
                                                        if (!isSystemAccount) {
                                                                val selectedPinMessageId =
                                                                        remember(selectedMessages, messages) {
                                                                                if (selectedMessages.size != 1) {
                                                                                        null
                                                                                } else {
                                                                                        messages
                                                                                                .firstOrNull {
                                                                                                        selectedMessages.contains(it.id) &&
                                                                                                                it.attachments.none { attachment ->
                                                                                                                        attachment.type == AttachmentType.AVATAR
                                                                                                                }
                                                                                                }
                                                                                                ?.id
                                                                                }
                                                                        }
                                                                val selectedPinMessageIsPinned =
                                                                        remember(selectedPinMessageId, pinnedMessages) {
                                                                                selectedPinMessageId != null &&
                                                                                        pinnedMessages.any { it.messageId == selectedPinMessageId }
                                                                        }
                                                                val canToggleSelectedPin = selectedPinMessageId != null
                                                                // SELECTION ACTION BAR - Reply/Forward
                                                                Column(
                                                                modifier =
                                                                        Modifier.fillMaxWidth()
                                                                                .background(
                                                                                        backgroundColor
                                                                                )
                                                        ) {
                                                                // Border сверху
                                                                Box(
                                                                        modifier =
                                                                                Modifier.fillMaxWidth()
                                                                                        .height(
                                                                                                0.5.dp
                                                                                        )
                                                                                        .background(
                                                                                                if (isDarkTheme
                                                                                                )
                                                                                                        Color.White
                                                                                                                .copy(
                                                                                                                        alpha =
                                                                                                                                0.15f
                                                                                                                )
                                                                                                else
                                                                                                        Color.Black
                                                                                                                .copy(
                                                                                                                        alpha =
                                                                                                                                0.1f
                                                                                                                )
                                                                                        )
                                                                )

                                                                val buttonScale by
                                                                        animateFloatAsState(
                                                                                targetValue =
                                                                                        if (selectionMode
                                                                                        )
                                                                                                1f
                                                                                        else 0.95f,
                                                                                animationSpec =
                                                                                        spring(
                                                                                                dampingRatio =
                                                                                                        Spring.DampingRatioNoBouncy,
                                                                                                stiffness =
                                                                                                        Spring.StiffnessMediumLow
                                                                                        ),
                                                                                label =
                                                                                        "buttonScale"
                                                                        )

                                                                Row(
                                                                        modifier =
                                                                                Modifier.fillMaxWidth()
                                                                                        .padding(
                                                                                                start =
                                                                                                        12.dp,
                                                                                                end =
                                                                                                        12.dp,
                                                                                                top =
                                                                                                        8.dp,
                                                                                                bottom =
                                                                                                        16.dp
                                                                                        )
                                                                                        .then(
                                                                                                if (hasNativeNavigationBar) Modifier.navigationBarsPadding()
                                                                                                else Modifier
                                                                                        )
                                                                                        .graphicsLayer {
                                                                                                scaleX =
                                                                                                        buttonScale
                                                                                                scaleY =
                                                                                                        buttonScale
                                                                                        },
                                                                        horizontalArrangement =
                                                                                Arrangement
                                                                                        .spacedBy(
                                                                                                12.dp
                                                                                        ),
                                                                        verticalAlignment =
                                                                                Alignment
                                                                                        .CenterVertically
                                                                ) {
                                                                        // Reply button
                                                                        Box(
                                                                                modifier =
                                                                                        Modifier.weight(
                                                                                                        1f
                                                                                                )
                                                                                                .height(
                                                                                                        48.dp
                                                                                                )
                                                                                                .clip(
                                                                                                        RoundedCornerShape(
                                                                                                                12.dp
                                                                                                        )
                                                                                                )
                                                                                                .background(
                                                                                                        PrimaryBlue
                                                                                                                .copy(
                                                                                                                        alpha =
                                                                                                                                0.1f
                                                                                                                )
                                                                                                )
                                                                                                .clickable {
                                                                                                        val selectedMsgs =
                                                                                                                messages
                                                                                                                        .filter {
                                                                                                                               selectedMessages
                                                                                                                                        .contains(it.id) &&
                                                                                                                                        it.attachments.none { attachment ->
                                                                                                                                                attachment.type == AttachmentType.AVATAR
                                                                                                                                        }
                                                                                                                        }
                                                                                                                        .sortedWith(
                                                                                                                                compareBy<ChatMessage>(
                                                                                                                                        { it.timestamp.time },
                                                                                                                                        { it.id }
                                                                                                                                )
                                                                                                                        )
                                                                                                        messagesViewModel
                                                                                                                .setReplyMessages(
                                                                                                                        selectedMsgs
                                                                                                                )
                                                                                                        selectedMessages =
                                                                                                                emptySet()
                                                                                                },
                                                                                contentAlignment =
                                                                                        Alignment
                                                                                                .Center
                                                                        ) {
                                                                                Row(
                                                                                        verticalAlignment =
                                                                                                Alignment
                                                                                                        .CenterVertically,
                                                                                        horizontalArrangement =
                                                                                                Arrangement
                                                                                                        .Center
                                                                                ) {
                                                                                        Icon(
                                                                                                Icons.Default
                                                                                                        .Reply,
                                                                                                contentDescription =
                                                                                                        null,
                                                                                                tint =
                                                                                                        PrimaryBlue,
                                                                                                modifier =
                                                                                                        Modifier.size(
                                                                                                                20.dp
                                                                                                        )
                                                                                        )
                                                                                        Spacer(
                                                                                                modifier =
                                                                                                        Modifier.width(
                                                                                                                8.dp
                                                                                                        )
                                                                                        )
                                                                                        Text(
                                                                                                "Reply",
                                                                                                color =
                                                                                                        PrimaryBlue,
                                                                                                fontSize =
                                                                                                        15.sp,
                                                                                                fontWeight =
                                                                                                        FontWeight
                                                                                                                .SemiBold
                                                                                        )
                                                                                }
                                                                        }

                                                                        // Forward button
                                                                        Box(
                                                                                modifier =
                                                                                        Modifier.weight(
                                                                                                        1f
                                                                                                )
                                                                                                .height(
                                                                                                        48.dp
                                                                                                )
                                                                                                .clip(
                                                                                                        RoundedCornerShape(
                                                                                                                12.dp
                                                                                                        )
                                                                                                )
                                                                                                .background(
                                                                                                        PrimaryBlue
                                                                                                                .copy(
                                                                                                                        alpha =
                                                                                                                                0.1f
                                                                                                                )
                                                                                                )
                                                                                                .clickable {
                                                                                                        val selectedMsgs =
                                                                                                                messages
                                                                                                                        .filter {
                                                                                                                               selectedMessages
                                                                                                                                        .contains(it.id) &&
                                                                                                                                        it.attachments.none { attachment ->
                                                                                                                                                attachment.type == AttachmentType.AVATAR
                                                                                                                                        }
                                                                                                                        }
                                                                                                                        .sortedWith(
                                                                                                                                compareBy<ChatMessage>(
                                                                                                                                        { it.timestamp.time },
                                                                                                                                        { it.id }
                                                                                                                                )
                                                                                                                        )

                                                                                                        val forwardMessages =
                                                                                                                selectedMsgs
                                                                                                                        .flatMap {
                                                                                                                                msg
                                                                                                                                ->
                                                                                                                                // Re-forward: preserve original senders
                                                                                                                                if (msg.forwardedMessages.isNotEmpty()) {
                                                                                                                                        msg.forwardedMessages.map { fwd ->
                                                                                                                                                ForwardManager
                                                                                                                                                        .ForwardMessage(
                                                                                                                                                                messageId =
                                                                                                                                                                        fwd.messageId,
                                                                                                                                                                text =
                                                                                                                                                                        fwd.text,
                                                                                                                                                                timestamp =
                                                                                                                                                                        msg.timestamp
                                                                                                                                                                                .time,
                                                                                                                                                                isOutgoing =
                                                                                                                                                                        fwd.isFromMe,
                                                                                                                                                                senderPublicKey =
                                                                                                                                                                        fwd.senderPublicKey.ifEmpty {
                                                                                                                                                                                if (fwd.isFromMe) currentUserPublicKey else user.publicKey
                                                                                                                                                                        },
                                                                                                                                                                originalChatPublicKey =
                                                                                                                                                                        user.publicKey,
                                                                                                                                                                senderName =
                                                                                                                                                                        fwd.forwardedFromName.ifEmpty { fwd.senderName.ifEmpty { "User" } },
                                                                                                                                                                attachments =
                                                                                                                                                                        fwd.attachments
                                                                                                                                                                                .filter {
                                                                                                                                                                                        it.type !=
                                                                                                                                                                                                AttachmentType
                                                                                                                                                                                                        .MESSAGES
                                                                                                                                                                                 },
                                                                                                                                                                chachaKeyPlain = fwd.chachaKeyPlainHex
                                                                                                                                                        )
                                                                                                                                        }
                                                                                                                                } else {
                                                                                                                                        listOf(ForwardManager
                                                                                                                                                .ForwardMessage(
                                                                                                                                                        messageId =
                                                                                                                                                                msg.id,
                                                                                                                                                        text =
                                                                                                                                                                msg.text,
                                                                                                                                                        timestamp =
                                                                                                                                                                msg.timestamp
                                                                                                                                                                        .time,
                                                                                                                                                        isOutgoing =
                                                                                                                                                                msg.isOutgoing,
                                                                                                                                                        senderPublicKey =
                                                                                                                                                                if (msg.isOutgoing
                                                                                                                                                                )
                                                                                                                                                                        currentUserPublicKey
                                                                                                                                                                else
                                                                                                                                                                        user.publicKey,
                                                                                                                                                        originalChatPublicKey =
                                                                                                                                                                user.publicKey,
                                                                                                                                                        senderName =
                                                                                                                                                                if (msg.isOutgoing) currentUserName.ifEmpty { "You" }
                                                                                                                                                                else user.title.ifEmpty { user.username.ifEmpty { "User" } },
                                                                                                                                                        attachments =
                                                                                                                                                                msg.attachments
                                                                                                                                                                        .filter {
                                                                                                                                                                                it.type !=
                                                                                                                                                                                        AttachmentType
                                                                                                                                                                                                .MESSAGES
                                                                                                                                                                         },
                                                                                                                                                        chachaKeyPlain = msg.chachaKeyPlainHex
                                                                                                                                                ))
                                                                                                                                }
                                                                                                                        }
                                                                                                        ForwardManager
                                                                                                                .setForwardMessages(
                                                                                                                        forwardMessages,
                                                                                                                        showPicker =
                                                                                                                                false
                                                                                                                )
                                                                                                        selectedMessages =
                                                                                                                emptySet()
                                                                                                        showForwardPicker =
                                                                                                                true
                                                                                                },
                                                                                contentAlignment =
                                                                                        Alignment
                                                                                                .Center
                                                                        ) {
                                                                                Row(
                                                                                        verticalAlignment =
                                                                                                Alignment
                                                                                                        .CenterVertically,
                                                                                        horizontalArrangement =
                                                                                                Arrangement
                                                                                                        .Center
                                                                                ) {
                                                                                        Icon(
                                                                                                Icons.Default
                                                                                                        .Forward,
                                                                                                contentDescription =
                                                                                                        null,
                                                                                                tint =
                                                                                                        PrimaryBlue,
                                                                                                modifier =
                                                                                                        Modifier.size(
                                                                                                                20.dp
                                                                                                        )
                                                                                        )
                                                                                        Spacer(
                                                                                                modifier =
                                                                                                        Modifier.width(
                                                                                                                8.dp
                                                                                                        )
                                                                                        )
                                                                                        Text(
                                                                                                "Forward",
                                                                                                color =
                                                                                                        PrimaryBlue,
                                                                                                fontSize =
                                                                                                        15.sp,
                                                                                                fontWeight =
                                                                                                        FontWeight
                                                                                                                .SemiBold
                                                                                        )
                                                                                }
                                                                        }

                                                                }
                                                        }
                                                }
                                                } else if (!isSystemAccount) {
                                                                // INPUT BAR
                                                        Column {
                                                                ChatInputBarSection(
                                                                        messagesViewModel = messagesViewModel,
                                                                        typingViewModel = typingViewModel,
                                                                        isSavedMessages = isSavedMessages,
                                                                        onSend = {
                                                                                isSendingMessage = true
                                                                                messagesViewModel.ensureSendContext(
                                                                                        publicKey = user.publicKey,
                                                                                        title = user.title,
                                                                                        username = user.username,
                                                                                        verified = user.verified
                                                                                )
                                                                                messagesViewModel.sendMessage()
                                                                                scope.launch {
                                                                                        delay(100)
                                                                                        listState.animateScrollToItem(0)
                                                                                        delay(300)
                                                                                        isSendingMessage = false
                                                                                }
                                                                        },
                                                                        onSendVoiceMessage = { voiceHex, durationSec, waves ->
                                                                                isSendingMessage = true
                                                                                messagesViewModel.ensureSendContext(
                                                                                        publicKey = user.publicKey,
                                                                                        title = user.title,
                                                                                        username = user.username,
                                                                                        verified = user.verified
                                                                                )
                                                                                voiceRecordingViewModel.sendVoiceMessage(
                                                                                        voiceHex = voiceHex,
                                                                                        durationSec = durationSec,
                                                                                        waves = waves
                                                                                )
                                                                                scope.launch {
                                                                                        delay(120)
                                                                                        listState.animateScrollToItem(0)
                                                                                        delay(220)
                                                                                        isSendingMessage = false
                                                                                }
                                                                        },
                                                                        isDarkTheme = isDarkTheme,
                                                                        backgroundColor = backgroundColor,
                                                                        textColor = textColor,
                                                                        secondaryTextColor =
                                                                                secondaryTextColor,
                                                                        replyMessages = replyMessages,
                                                                        isForwardMode = isForwardMode,
                                                                        onCloseReply = {
                                                                                messagesViewModel.clearReplyMessages()
                                                                        },
                                                                        onShowForwardOptions = { panelMessages ->
                                                                                if (panelMessages.isEmpty()) {
                                                                                        return@ChatInputBarSection
                                                                                }
                                                                                val forwardMessages =
                                                                                        panelMessages.map { msg ->
                                                                                                ForwardManager.ForwardMessage(
                                                                                                        messageId = msg.messageId,
                                                                                                        text = msg.text,
                                                                                                        timestamp = msg.timestamp,
                                                                                                        isOutgoing = msg.isOutgoing,
                                                                                                        senderPublicKey =
                                                                                                                msg.publicKey.ifEmpty {
                                                                                                                        if (msg.isOutgoing) currentUserPublicKey
                                                                                                                        else user.publicKey
                                                                                                                },
                                                                                                        originalChatPublicKey =
                                                                                                                user.publicKey,
                                                                                                        senderName =
                                                                                                                msg.senderName.ifEmpty {
                                                                                                                        if (msg.isOutgoing) currentUserName.ifEmpty { "You" }
                                                                                                                        else user.title.ifEmpty { user.username.ifEmpty { "User" } }
                                                                                                                },
                                                                                                        attachments =
                                                                                                                msg.attachments
                                                                                                                        .filter {
                                                                                                                                it.type != AttachmentType.MESSAGES
                                                                                                                        }
                                                                                                                        .map { it.copy(localUri = "") },
                                                                                                        chachaKeyPlain = msg.chachaKeyPlainHex
                                                                                                )
                                                                                        }

                                                                                ForwardManager.setForwardMessages(
                                                                                        forwardMessages,
                                                                                        showPicker = false
                                                                                )
                                                                                showForwardPicker = true
                                                                        },
                                                                        chatTitle = chatTitle,
                                                                        isBlocked = isBlocked,
                                                                        showEmojiPicker = showEmojiPicker,
                                                                        onToggleEmojiPicker = {
                                                                                showEmojiPicker = it
                                                                        },
                                                                        coordinator = coordinator,
                                                                        displayReplyMessages =
                                                                                displayReplyMessages,
                                                                        onReplyClick = scrollToMessage,
                                                                        onAttachClick = {
                                                                                // Telegram-style:
                                                                                // галерея открывается поверх клавиатуры.
                                                                                showMediaPicker = true
                                                                        },
                                                                        myPublicKey =
                                                                                messagesViewModel.myPublicKey ?: "",
                                                                        opponentPublicKey = user.publicKey,
                                                                        myPrivateKey = currentUserPrivateKey,
                                                                        isGroupChat = isGroupChat,
                                                                        mentionCandidates = mentionCandidates,
                                                                        avatarRepository = avatarRepository,
                                                                        inputFocusTrigger = inputFocusTrigger,
                                                                        suppressKeyboard = showInAppCamera,
                                                                        hasNativeNavigationBar =
                                                                                hasNativeNavigationBar
                                                                )
                                                        }
                                                }
                                        }
                                } // Закрытие Column с imePadding
                        }
                ) { paddingValues ->
                        // 🔥 Box wrapper для overlay (MediaPicker над клавиатурой)
                        Box(modifier = Modifier.fillMaxSize()) {
                                // Keep wallpaper on a fixed full-screen layer so it doesn't rescale
                                // when content paddings (bottom bar/IME) change.
                                if (chatWallpaperResId != null) {
                                        ChatWallpaperBackground(
                                                wallpaperResId = chatWallpaperResId,
                                                modifier = Modifier.matchParentSize()
                                        )
                                } else {
                                        Box(
                                                modifier =
                                                        Modifier.matchParentSize()
                                                                .background(backgroundColor)
                                        )
                                }

                                Box(modifier = Modifier.fillMaxSize().padding(paddingValues)) {
                                        // 🔥 Column структура - список сжимается когда клавиатура
                                        // открывается
                                        Column(
                                                modifier = Modifier.fillMaxSize()
                                        ) {
                                        // Список сообщений - занимает всё доступное место
                                        Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
                                                // Плавная анимация bottom padding при входе/выходе из selection mode
                                                val animatedListBottomPadding by animateDpAsState(
                                                        targetValue = if (isSelectionMode) 16.dp else 16.dp,
                                                        animationSpec = tween(250),
                                                        label = "listBottomPadding"
                                                )
                                                when {
                                                        groupRequiresRejoin -> {
                                                                GroupRejoinRequiredState(
                                                                        isDarkTheme = isDarkTheme,
                                                                        hasChatWallpaper = hasChatWallpaper,
                                                                        modifier = Modifier.fillMaxSize()
                                                                )
                                                        }
                                                        // 🔥 СКЕЛЕТОН - показываем пока загружаются
                                                        // сообщения
                                                        showMessageSkeleton -> {
                                                                MessageSkeletonList(
                                                                        isDarkTheme = isDarkTheme,
                                                                        isGroupChat = isGroupChat,
                                                                        modifier =
                                                                                Modifier.fillMaxSize()
                                                                )
                                                        }
                                                        isLoading && messages.isEmpty() -> {
                                                                Box(modifier = Modifier.fillMaxSize())
                                                        }
                                                        // Пустое состояние (нет сообщений)
                                                        messages.isEmpty() -> {
                                                                Column(
                                                                        modifier =
                                                                                Modifier.fillMaxSize()
                                                                                        .padding(
                                                                                                32.dp
                                                                                        ),
                                                                        horizontalAlignment =
                                                                                Alignment
                                                                                        .CenterHorizontally,
                                                                        verticalArrangement =
                                                                                Arrangement.Center
                                                                ) {
                                                                        val showSavedMessagesBackdrop =
                                                                                isSavedMessages &&
                                                                                        hasChatWallpaper
                                                                        val savedMessagesBackdropShape =
                                                                                RoundedCornerShape(
                                                                                        22.dp
                                                                                )
                                                                        val savedMessagesBackdropColor =
                                                                                if (isDarkTheme)
                                                                                        Color(
                                                                                                0xB3212121
                                                                                        )
                                                                                else
                                                                                        Color(
                                                                                                0xB32A2A2A
                                                                                        )
                                                                        val contentModifier =
                                                                                if (
                                                                                        showSavedMessagesBackdrop
                                                                                ) {
                                                                                        Modifier
                                                                                                .widthIn(
                                                                                                        max =
                                                                                                                340.dp
                                                                                                )
                                                                                                .fillMaxWidth()
                                                                                                .background(
                                                                                                        color =
                                                                                                                savedMessagesBackdropColor,
                                                                                                        shape =
                                                                                                                savedMessagesBackdropShape
                                                                                                )
                                                                                                .border(
                                                                                                        width =
                                                                                                                1.dp,
                                                                                                        color =
                                                                                                                Color.White
                                                                                                                        .copy(
                                                                                                                                alpha =
                                                                                                                                        0.12f
                                                                                                                        ),
                                                                                                        shape =
                                                                                                                savedMessagesBackdropShape
                                                                                                )
                                                                                                .padding(
                                                                                                        horizontal =
                                                                                                                22.dp,
                                                                                                        vertical =
                                                                                                                18.dp
                                                                                                )
                                                                                } else {
                                                                                        Modifier
                                                                                }

                                                                        Column(
                                                                                modifier =
                                                                                        contentModifier,
                                                                                horizontalAlignment =
                                                                                        Alignment
                                                                                                .CenterHorizontally
                                                                        ) {
                                                                                val emptyStateTextAlign =
                                                                                        if (isSavedMessages)
                                                                                                TextAlign.Center
                                                                                        else
                                                                                                TextAlign.Start
                                                                                val emptyStateTextModifier =
                                                                                        if (isSavedMessages)
                                                                                                Modifier.fillMaxWidth()
                                                                                        else
                                                                                                Modifier
                                                                                if (isSavedMessages) {
                                                                                        val composition by
                                                                                                rememberLottieComposition(
                                                                                                        LottieCompositionSpec
                                                                                                                .RawRes(
                                                                                                                        R.raw.saved
                                                                                                                )
                                                                                                )
                                                                                        val progress by
                                                                                                animateLottieCompositionAsState(
                                                                                                        composition =
                                                                                                                composition,
                                                                                                        iterations =
                                                                                                                LottieConstants
                                                                                                                        .IterateForever
                                                                                                )
                                                                                        LottieAnimation(
                                                                                                composition =
                                                                                                        composition,
                                                                                                progress = {
                                                                                                        progress
                                                                                                },
                                                                                                modifier =
                                                                                                        Modifier.size(
                                                                                                                120.dp
                                                                                                        )
                                                                                        )
                                                                                } else {
                                                                                        val composition by
                                                                                                rememberLottieComposition(
                                                                                                        LottieCompositionSpec
                                                                                                                .RawRes(
                                                                                                                        R.raw.speech
                                                                                                                )
                                                                                                )
                                                                                        val progress by
                                                                                                animateLottieCompositionAsState(
                                                                                                        composition =
                                                                                                                composition,
                                                                                                        iterations =
                                                                                                                LottieConstants
                                                                                                                        .IterateForever
                                                                                                )
                                                                                        LottieAnimation(
                                                                                                composition =
                                                                                                        composition,
                                                                                                progress = {
                                                                                                        progress
                                                                                                },
                                                                                                modifier =
                                                                                                        Modifier.size(
                                                                                                                120.dp
                                                                                                        )
                                                                                        )
                                                                                }
                                                                                Spacer(
                                                                                        modifier =
                                                                                                Modifier.height(
                                                                                                        16.dp
                                                                                                )
                                                                                )
                                                                                Text(
                                                                                        text =
                                                                                                if (isSavedMessages
                                                                                                )
                                                                                                        "Save messages here for quick access"
                                                                                                else
                                                                                                        "No messages yet",
                                                                                        fontSize =
                                                                                                16.sp,
                                                                                        color =
                                                                                                dateHeaderTextColor,
                                                                                        fontWeight =
                                                                                                FontWeight
                                                                                                        .Medium,
                                                                                        textAlign =
                                                                                                emptyStateTextAlign,
                                                                                        modifier =
                                                                                                emptyStateTextModifier
                                                                                )
                                                                                Spacer(
                                                                                        modifier =
                                                                                                Modifier.height(
                                                                                                        8.dp
                                                                                                )
                                                                                )
                                                                                Text(
                                                                                        text =
                                                                                                if (isSavedMessages
                                                                                                )
                                                                                                        "Forward messages here or send notes to yourself"
                                                                                                else
                                                                                                        "Send a message to start the conversation",
                                                                                        fontSize =
                                                                                                14.sp,
                                                                                        color =
                                                                                                dateHeaderTextColor
                                                                                                        .copy(
                                                                                                                alpha =
                                                                                                                        0.7f
                                                                                                        ),
                                                                                        textAlign =
                                                                                                emptyStateTextAlign,
                                                                                        modifier =
                                                                                                emptyStateTextModifier
                                                                                )
                                                                        }
                                                                }
                                                        }
                                                        // Есть сообщения
                                                        else -> {
                                                                LazyColumn(
                                                                        state = listState,
                                                                        userScrollEnabled = !textSelectionHelper.movingHandle,
                                                                        modifier =
                                                                                Modifier.fillMaxSize()
                                                                                        .nestedScroll(
                                                                                                remember {
                                                                                                        object :
                                                                                                                NestedScrollConnection {
                                                                                                                override fun onPreScroll(
                                                                                                                        available:
                                                                                                                                Offset,
                                                                                                                        source:
                                                                                                                                NestedScrollSource
                                                                                                                ): Offset {
                                                                                                                        // Отслеживаем ручную
                                                                                                                        // прокрутку
                                                                                                                        // пользователем
                                                                                                                        if (source ==
                                                                                                                                        NestedScrollSource
                                                                                                                                                .Drag
                                                                                                                        ) {
                                                                                                                                wasManualScroll =
                                                                                                                                        true
                                                                                                                        }
                                                                                                                        return Offset.Zero
                                                                                                                }
                                                                                                        }
                                                                                                }
                                                                                        ),
                                                                        contentPadding =
                                                                                PaddingValues(
                                                                                        start =
                                                                                                0.dp,
                                                                                        end = 0.dp,
                                                                                        top = 8.dp,
                                                                                        bottom =
                                                                                                animatedListBottomPadding
                                                                                ),
                                                                        reverseLayout = true,
                                                                        verticalArrangement = Arrangement.Bottom
                                                                ) {
                                                                        itemsIndexed(
                                                                                messagesWithDates,
                                                                                key = { _, item ->
                                                                                        item.first
                                                                                                .id
                                                                                },
                                                                                contentType = { _, item ->
                                                                                        val hasAttachments =
                                                                                                item.first.attachments
                                                                                                        .isNotEmpty()
                                                                                        when {
                                                                                                item.second -> "message_with_date"
                                                                                                hasAttachments -> "message_with_attachments"
                                                                                                else -> "message_text_only"
                                                                                        }
                                                                                }
                                                                        ) {
                                                                                index,
                                                                                (message, showDate)
                                                                                ->
                                                                                val prevMessage =
                                                                                        messagesWithDates
                                                                                                .getOrNull(
                                                                                                        index -
                                                                                                                1
                                                                                                )
                                                                                                ?.first
                                                                                val nextMessage =
                                                                                        messagesWithDates
                                                                                                .getOrNull(
                                                                                                        index +
                                                                                                                1
                                                                                                )
                                                                                                ?.first
                                                                                val senderPublicKeyForMessage =
                                                                                        resolveSenderPublicKey(
                                                                                                message
                                                                                        )
                                                                                // Для reverseLayout + DESC списка:
                                                                                // prev = более новое сообщение,
                                                                                // next = более старое.
                                                                                val showTail =
                                                                                        isMessageBoundary(message, prevMessage)
                                                                                val isGroupStart =
                                                                                        isMessageBoundary(message, nextMessage)
                                                                                val runHeadIndex =
                                                                                        messageRunNewestIndex.getOrNull(
                                                                                                index
                                                                                        ) ?: index
                                                                                val runTailIndex =
                                                                                        messageRunOldestIndexByHead
                                                                                                .getOrNull(
                                                                                                        runHeadIndex
                                                                                                )
                                                                                                ?: runHeadIndex
                                                                                val isHeadPhase =
                                                                                        incomingRunAvatarUiState
                                                                                                .showOnRunHeads
                                                                                                .contains(
                                                                                                        runHeadIndex
                                                                                                )
                                                                                val isTailPhase =
                                                                                        incomingRunAvatarUiState
                                                                                                .showOnRunTails
                                                                                                .contains(
                                                                                                        runHeadIndex
                                                                                                )
                                                                                val showIncomingGroupAvatar =
                                                                                        isGroupChat &&
                                                                                                !message.isOutgoing &&
                                                                                                senderPublicKeyForMessage
                                                                                                        .isNotBlank() &&
                                                                                                ((index ==
                                                                                                                        runHeadIndex &&
                                                                                                                        isHeadPhase &&
                                                                                                                        showTail) ||
                                                                                                        (index ==
                                                                                                                        runTailIndex &&
                                                                                                                        isTailPhase &&
                                                                                                                        isGroupStart))

                                                                                val isDeleting = message.id in pendingDeleteIds
                                                                                val messageItemContent: @Composable () -> Unit = {
                                                                                Column {
                                                                                        if (showDate
                                                                                        ) {
                                                                                                DateHeader(
                                                                                                        dateText =
                                                                                                                getDateText(
                                                                                                                        message.timestamp
                                                                                                                                .time
                                                                                                                ),
                                                                                                        textColor =
                                                                                                                dateHeaderTextColor,
                                                                                                        backgroundColor =
                                                                                                                dateHeaderBackgroundColor
                                                                                                )
                                                                                        }
                                                                                        val selectionKey =
                                                                                                message.id
                                                                                        MessageBubble(
                                                                                                message =
                                                                                                        message,
                                                                                                textSelectionHelper =
                                                                                                        textSelectionHelper,
                                                                                                isDarkTheme =
                                                                                                        isDarkTheme,
                                                                                                hasWallpaper =
                                                                                                        hasChatWallpaper,
                                                                                                isSystemSafeChat =
                                                                                                        isSystemAccount,
                                                                                                isSelectionMode =
                                                                                                        isSelectionMode,
                                                                                                showTail =
                                                                                                        showTail,
                                                                                                showIncomingGroupAvatar =
                                                                                                        showIncomingGroupAvatar,
                                                                                                isGroupStart =
                                                                                                        isGroupStart,
                                                                                                isSelected =
                                                                                                        selectedMessages
                                                                                                                .contains(
                                                                                                                        selectionKey
                                                                                                                ),
                                                                                                isHighlighted =
                                                                                                        highlightedMessageId ==
                                                                                                                message.id,
                                                                                                isSavedMessages =
                                                                                                        isSavedMessages,
                                                                                                privateKey =
                                                                                                        currentUserPrivateKey,
                                                                                                senderPublicKey =
                                                                                                        senderPublicKeyForMessage,
                                                                                                senderName =
                                                                                                        message.senderName,
                                                                                                isGroupChat =
                                                                                                        isGroupChat,
                                                                                                dialogPublicKey =
                                                                                                        user.publicKey,
                                                                                                showGroupSenderLabel =
                                                                                                        isGroupChat &&
                                                                                                                !message.isOutgoing &&
                                                                                                                isGroupStart,
                                                                                                isGroupSenderAdmin =
                                                                                                        isGroupChat &&
                                                                                                                senderPublicKeyForMessage
                                                                                                                        .isNotBlank() &&
                                                                                                                groupAdminKeys
                                                                                                                        .contains(
                                                                                                                                senderPublicKeyForMessage
                                                                                                                        ),
                                                                                                deferHeavyAttachmentsUntilReady =
                                                                                                        useLightweightColdOpen,
                                                                                                currentUserPublicKey =
                                                                                                        currentUserPublicKey,
                                                                                                currentUserUsername =
                                                                                                        currentUserUsername,
                                                                                                groupRepository =
                                                                                                        groupRepository,
                                                                                                avatarRepository =
                                                                                                        avatarRepository,
                                                                                                onLongClick = {
                                                                                                        if (simplePickerPreviewUri != null || isCallActive) {
                                                                                                                return@MessageBubble
                                                                                                        }
                                                                                                        // 📳 Haptic feedback при долгом нажатии
                                                                                                        // Не разрешаем выделять avatar-сообщения
                                                                                                        val hasAvatar =
                                                                                                                message.attachments
                                                                                                                        .any {
                                                                                                                                it.type ==
                                                                                                                                        AttachmentType
                                                                                                                                                .AVATAR
                                                                                                                        }
                                                                                                        if (hasAvatar) return@MessageBubble

                                                                                                        hapticFeedback
                                                                                                                .performHapticFeedback(
                                                                                                                        HapticFeedbackType
                                                                                                                                .LongPress
                                                                                                                )

                                                                                                        // � Long press = selection mode
                                                                                                        val imm =
                                                                                                                context.getSystemService(
                                                                                                                        Context.INPUT_METHOD_SERVICE
                                                                                                                ) as
                                                                                                                        InputMethodManager
                                                                                                        imm.hideSoftInputFromWindow(
                                                                                                                view.windowToken,
                                                                                                                0
                                                                                                        )
                                                                                                        focusManager
                                                                                                                .clearFocus()
                                                                                                        showEmojiPicker =
                                                                                                                false
                                                                                                        selectMessageOnLongPress(
                                                                                                                selectionKey,
                                                                                                                true
                                                                                                        )
                                                                                                        suppressTapAfterLongPress(
                                                                                                                selectionKey
                                                                                                        )
                                                                                                },
                                                                                                onClick = {
                                                                                                        if (simplePickerPreviewUri != null || isCallActive) {
                                                                                                                return@MessageBubble
                                                                                                        }
                                                                                                        if (shouldIgnoreTapAfterLongPress(
                                                                                                                        selectionKey
                                                                                                                )
                                                                                                        ) {
                                                                                                                return@MessageBubble
                                                                                                        }
                                                                                                        val hasAvatar =
                                                                                                                message.attachments
                                                                                                                        .any {
                                                                                                                                it.type ==
                                                                                                                                        AttachmentType
                                                                                                                                                .AVATAR
                                                                                                                        }
                                                                                                        val isPhotoOnly =
                                                                                                                message.attachments.isNotEmpty() &&
                                                                                                                        message.text.isEmpty() &&
                                                                                                                        message.attachments.all {
                                                                                                                                it.type == AttachmentType.IMAGE
                                                                                                                        }
                                                                                                        val isCallMessage =
                                                                                                                message.attachments.isNotEmpty() &&
                                                                                                                        message.attachments.all {
                                                                                                                                it.type == AttachmentType.CALL
                                                                                                                        }
                                                                                                        if (isSelectionMode) {
                                                                                                                toggleMessageSelection(
                                                                                                                        selectionKey,
                                                                                                                        !hasAvatar
                                                                                                                )
                                                                                                        } else if (!hasAvatar && (!isPhotoOnly || isCallMessage)) {
                                                                                                                // 💬 Tap = context menu
                                                                                                                contextMenuMessage = message
                                                                                                                showContextMenu = true
                                                                                                                scope.launch {
                                                                                                                        contextMenuIsPinned = messagesViewModel.isMessagePinned(message.id)
                                                                                                                }
                                                                                                        }
                                                                                                },
                                                                                                onSwipeToReply = {
                                                                                                        // Не разрешаем reply на сообщения с аватаркой
                                                                                                        val hasAvatar =
                                                                                                                message.attachments
                                                                                                                        .any {
                                                                                                                                it.type ==
                                                                                                                                        AttachmentType
                                                                                                                                                .AVATAR
                                                                                                                        }
                                                                                                        if (!hasAvatar &&
                                                                                                                        !isSystemAccount
                                                                                                        ) {
                                                                                                                messagesViewModel
                                                                                                                        .setReplyMessages(listOf(message))
                                                                                                        }
                                                                                                },
                                                                                                onVoiceWaveGestureActiveChanged = { active ->
                                                                                                        isVoiceWaveGestureActive = active
                                                                                                },
                                                                                                onReplyClick = {
                                                                                                        messageId
                                                                                                        ->
                                                                                                        scrollToMessage(
                                                                                                                messageId
                                                                                                        )
                                                                                                },
                                                                                                onRetry = {
                                                                                                        messagesViewModel
                                                                                                                .retryMessage(message)
                                                                                                },
                                                                                                onDelete = {
                                                                                                        messagesViewModel
                                                                                                                .deleteMessage(message.id)
                                                                                                },
                                                                                                onCancelPhotoUpload = {
                                                                                                        attachmentId ->
                                                                                                        attachmentsViewModel
                                                                                                                .cancelOutgoingImageUpload(
                                                                                                                        message.id,
                                                                                                                        attachmentId
                                                                                                                )
                                                                                                },
                                                                                                onImageClick = {
                                                                                                        attachmentId,
                                                                                                        bounds
                                                                                                        ->
                                                                                                        // 📸 Открыть просмотрщик фото с shared element animation
                                                                                                        // Фиксируем список на момент клика (защита от краша при новых сообщениях)
                                                                                                        val allImages =
                                                                                                                extractImagesFromMessages(
                                                                                                                        messages,
                                                                                                                        currentUserPublicKey,
                                                                                                                        user.publicKey,
                                                                                                                        user.title
                                                                                                                                .ifEmpty {
                                                                                                                                        "User"
                                                                                                                                }
                                                                                                                )
                                                                                                        imageViewerImages =
                                                                                                                allImages
                                                                                                        imageViewerInitialIndex =
                                                                                                                findImageIndex(
                                                                                                                        allImages,
                                                                                                                        attachmentId
                                                                                                                )
                                                                                                        imageViewerSourceBounds =
                                                                                                                bounds
                                                                                                        showImageViewer =
                                                                                                                true
                                                                                                        onImageViewerChanged(
                                                                                                                true
                                                                                                        )
                                                                                                },
                                                                                                onAvatarClick = {
                                                                                                        avatarOwnerPublicKey ->
                                                                                                        openProfileByPublicKey(
                                                                                                                avatarOwnerPublicKey
                                                                                                        )
                                                                                                },
                                                                                                onForwardedSenderClick = { senderPublicKey ->
                                                                                                        // Open profile of the forwarded message sender
                                                                                                        scope.launch {
                                                                                                                val resolvedUser = messagesViewModel.resolveUserForProfile(senderPublicKey)
                                                                                                                if (resolvedUser != null) {
                                                                                                                        showContextMenu = false
                                                                                                                        contextMenuMessage = null
                                                                                                                        onUserProfileClick(resolvedUser)
                                                                                                                }
                                                                                                        }
                                                                                                },
                                                                                                onMentionClick = { username ->
                                                                                                        val normalizedUsername =
                                                                                                                username.trim().trimStart('@').lowercase(Locale.ROOT)
                                                                                                        if (normalizedUsername.isBlank()) return@MessageBubble
                                                                                                        // Mention tap should not trigger bubble context-menu tap.
                                                                                                        suppressTapAfterLongPress(selectionKey)
                                                                                                        scope.launch {
                                                                                                                val normalizedCurrentUsername =
                                                                                                                        currentUserUsername.trim().trimStart('@').lowercase(Locale.ROOT)
                                                                                                                val normalizedOpponentUsername =
                                                                                                                        user.username.trim().trimStart('@').lowercase(Locale.ROOT)
                                                                                                                val targetPublicKey =
                                                                                                                        mentionCandidates
                                                                                                                                .firstOrNull {
                                                                                                                                        it.username.equals(
                                                                                                                                                normalizedUsername,
                                                                                                                                                ignoreCase = true
                                                                                                                                        )
                                                                                                                                }
                                                                                                                                ?.publicKey
                                                                                                                                ?.trim()
                                                                                                                                ?.takeIf { it.isNotBlank() }
                                                                                                                               ?: run {
                                                                                                                                        if (normalizedCurrentUsername == normalizedUsername && currentUserPublicKey.isNotBlank()) {
                                                                                                                                                currentUserPublicKey.trim()
                                                                                                                                        } else {
                                                                                                                                                if (normalizedOpponentUsername == normalizedUsername && user.publicKey.isNotBlank()) user.publicKey.trim()
                                                                                                                                                else ""
                                                                                                                                        }
                                                                                                                                }

                                                                                                                if (targetPublicKey.isBlank()) {
                                                                                                                        val resolvedByUsername =
                                                                                                                                messagesViewModel.resolveUserByUsername(normalizedUsername)
                                                                                                                        if (resolvedByUsername != null) {
                                                                                                                                showContextMenu = false
                                                                                                                                contextMenuMessage = null
                                                                                                                                onNavigateToChat(resolvedByUsername)
                                                                                                                        }
                                                                                                                        return@launch
                                                                                                                }

                                                                                                                if (targetPublicKey.equals(currentUserPublicKey.trim(), ignoreCase = true)) {
                                                                                                                        showContextMenu = false
                                                                                                                        contextMenuMessage = null
                                                                                                                        onNavigateToChat(
                                                                                                                                SearchUser(
                                                                                                                                        title = currentUserName.ifBlank { "You" },
                                                                                                                                        username = currentUserUsername.trim().trimStart('@'),
                                                                                                                                        publicKey = currentUserPublicKey.trim(),
                                                                                                                                        verified = 0,
                                                                                                                                        online = 0
                                                                                                                                )
                                                                                                                        )
                                                                                                                        return@launch
                                                                                                                }

                                                                                                                val resolvedUser = messagesViewModel.resolveUserForProfile(targetPublicKey)
                                                                                                                if (resolvedUser != null) {
                                                                                                                        showContextMenu = false
                                                                                                                        contextMenuMessage = null
                                                                                                                        onNavigateToChat(resolvedUser)
                                                                                                                }
                                                                                                        }
                                                                                                },
                                                                                                onGroupInviteOpen = { inviteGroup ->
                                                                                                        onNavigateToChat(inviteGroup)
                                                                                                },
                                                                                                contextMenuContent = {
                                                                                        // 💬 Context menu anchored to this bubble
                                                                                        if (showContextMenu && contextMenuMessage?.id == message.id) {
                                                                                                val msg = contextMenuMessage!!
                                                                                                        MessageContextMenu(
                                                                                                        expanded = true,
                                                                                                        onDismiss = {
                                                                                                                showContextMenu = false
                                                                                                                contextMenuMessage = null
                                                                                                        },
                                                                                                        isDarkTheme = isDarkTheme,
                                                                                                        isPinned = contextMenuIsPinned,
                                                                                                        isOutgoing = msg.isOutgoing,
                                                                                                        hasText = extractCopyableMessageText(msg).isNotBlank(),
                                                                                                        isSystemAccount = isSystemAccount,
                                                                                                        onReply = {
                                                                                                                messagesViewModel.setReplyMessages(listOf(msg))
                                                                                                                showContextMenu = false
                                                                                                                contextMenuMessage = null
                                                                                                        },
                                                                                                        onCopy = {
                                                                                                                clipboardManager.setText(
                                                                                                                        androidx.compose.ui.text.AnnotatedString(extractCopyableMessageText(msg))
                                                                                                                )
                                                                                                                showContextMenu = false
                                                                                                                contextMenuMessage = null
                                                                                                        },
                                                                                                        onForward = {
                                                                                                                val forwardMessages = if (msg.forwardedMessages.isNotEmpty()) {
                                                                                                                        msg.forwardedMessages.map { fwd ->
                                                                                                                                ForwardManager.ForwardMessage(
                                                                                                                                        messageId = fwd.messageId,
                                                                                                                                        text = fwd.text,
                                                                                                                                        timestamp = msg.timestamp.time,
                                                                                                                                        isOutgoing = fwd.isFromMe,
                                                                                                                                        senderPublicKey = fwd.senderPublicKey.ifEmpty {
                                                                                                                                                if (fwd.isFromMe) currentUserPublicKey else user.publicKey
                                                                                                                                        },
                                                                                                                                        originalChatPublicKey = user.publicKey,
                                                                                                                                        senderName = fwd.forwardedFromName.ifEmpty { fwd.senderName.ifEmpty { "User" } },
                                                                                                                                        attachments = fwd.attachments
                                                                                                                                                .filter { it.type != AttachmentType.MESSAGES }
                                                                                                                                                .map { it.copy(localUri = "") },
                                                                                                                                        chachaKeyPlain = fwd.chachaKeyPlainHex
                                                                                                                                )
                                                                                                                        }
                                                                                                                } else {
                                                                                                                        listOf(ForwardManager.ForwardMessage(
                                                                                                                                messageId = msg.id,
                                                                                                                                text = msg.text,
                                                                                                                                timestamp = msg.timestamp.time,
                                                                                                                                isOutgoing = msg.isOutgoing,
                                                                                                                                senderPublicKey = if (msg.isOutgoing) currentUserPublicKey else user.publicKey,
                                                                                                                                originalChatPublicKey = user.publicKey,
                                                                                                                                senderName = if (msg.isOutgoing) currentUserName.ifEmpty { "You" }
                                                                                                                                        else user.title.ifEmpty { user.username.ifEmpty { "User" } },
                                                                                                                                attachments = msg.attachments
                                                                                                                                        .filter { it.type != AttachmentType.MESSAGES }
                                                                                                                                        .map { it.copy(localUri = "") },
                                                                                                                                chachaKeyPlain = msg.chachaKeyPlainHex
                                                                                                                        ))
                                                                                                                }
                                                                                                                ForwardManager.setForwardMessages(forwardMessages, showPicker = false)
                                                                                                                showForwardPicker = true
                                                                                                                showContextMenu = false
                                                                                                                contextMenuMessage = null
                                                                                                        },
                                                                                                        onPin = {
                                                                                                                if (contextMenuIsPinned) {
                                                                                                                        messagesViewModel.unpinMessage(msg.id)
                                                                                                                } else {
                                                                                                                        messagesViewModel.pinMessage(msg.id)
                                                                                                                        isPinnedBannerDismissed = false
                                                                                                                }
                                                                                                                showContextMenu = false
                                                                                                                contextMenuMessage = null
                                                                                                        },
                                                                                                        onDelete = {
                                                                                                                messagesViewModel.deleteMessage(msg.id)
                                                                                                                showContextMenu = false
                                                                                                                contextMenuMessage = null
                                                                                                        }
                                                                                                )
                                                                                        }
                                                                                        } // contextMenuContent
                                                                                )
                                                                        }
                                                                }

                                                                if (pendingDeleteIds.isEmpty()) {
                                                                        if (!isDeleting) {
                                                                                messageItemContent()
                                                                        }
                                                                } else {
                                                                        androidx.compose.animation.AnimatedVisibility(
                                                                                visible = !isDeleting,
                                                                                exit =
                                                                                        androidx.compose.animation.shrinkVertically(
                                                                                                animationSpec =
                                                                                                        androidx.compose.animation.core.tween(
                                                                                                                250,
                                                                                                                easing =
                                                                                                                        androidx.compose.animation.core.FastOutSlowInEasing
                                                                                                        )
                                                                                        ) +
                                                                                                androidx.compose.animation.fadeOut(
                                                                                                        animationSpec =
                                                                                                                androidx.compose.animation.core.tween(
                                                                                                                        200
                                                                                                                )
                                                                                                )
                                                                        ) {
                                                                                messageItemContent()
                                                                        }
                                                                }
                                                }
                                                }
                                                androidx.compose.animation.AnimatedVisibility(
                                                        visible =
                                                                showFloatingDateHeader &&
                                                                        !isLoading &&
                                                                        !isSelectionMode,
                                                        enter =
                                                                fadeIn(animationSpec = tween(120)) +
                                                                        slideInVertically(
                                                                                animationSpec =
                                                                                        tween(120)
                                                                        ) { height ->
                                                                                -height / 2
                                                                        },
                                                        exit =
                                                                fadeOut(animationSpec = tween(100)) +
                                                                        slideOutVertically(
                                                                                animationSpec =
                                                                                        tween(100)
                                                                        ) { height ->
                                                                                -height / 2
                                                                        },
                                                        modifier =
                                                                Modifier.align(Alignment.TopCenter)
                                                                        .padding(top = 8.dp)
                                                                        .zIndex(3f)
                                                ) {
                                                        floatingDateText?.let { dateText ->
                                                                DateHeader(
                                                                        dateText = dateText,
                                                                        textColor = dateHeaderTextColor,
                                                                        backgroundColor =
                                                                                dateHeaderBackgroundColor,
                                                                        verticalPadding = 0.dp
                                                                )
                                                        }
                                                }
                                                if (incomingRunAvatarUiState.overlays.isNotEmpty()) {
                                                        val avatarInsetPx =
                                                                with(density) {
                                                                        incomingRunAvatarInsetStart
                                                                                .roundToPx()
                                                                }
                                                        Box(
                                                                modifier =
                                                                        Modifier.matchParentSize()
                                                                                .graphicsLayer {
                                                                                        clip = true
                                                                                }
                                                        ) {
                                                                incomingRunAvatarUiState.overlays.forEach { overlay ->
                                                                        key(
                                                                                overlay.runHeadIndex,
                                                                                overlay.senderPublicKey
                                                                        ) {
                                                                                Box(
                                                                                        modifier =
                                                                                                Modifier.offset {
                                                                                                        IntOffset(
                                                                                                                avatarInsetPx,
                                                                                                                overlay.topPx
                                                                                                                        .toInt()
                                                                                                        )
                                                                                                }
                                                                                ) {
                                                                                        AvatarImage(
                                                                                                publicKey =
                                                                                                        overlay.senderPublicKey,
                                                                                                avatarRepository =
                                                                                                        avatarRepository,
                                                                                                size =
                                                                                                        incomingRunAvatarSize,
                                                                                                isDarkTheme =
                                                                                                        isDarkTheme,
                                                                                                onClick =
                                                                                                        if (isSelectionMode) null
                                                                                                        else {
                                                                                                                {
                                                                                                                        openProfileByPublicKey(
                                                                                                                                overlay.senderPublicKey
                                                                                                                        )
                                                                                                                }
                                                                                                        },
                                                                                                displayName =
                                                                                                        overlay.senderDisplayName
                                                                                        )
                                                                                }
                                                                        }
                                                                }
                                                        }
                                                }
                                                // 🔤 Text selection overlay
                                                com.rosetta.messenger.ui.chats.components.TextSelectionOverlay(
                                                        helper = textSelectionHelper,
                                                        modifier = Modifier.fillMaxSize()
                                                )
                                                        }
                                        }
                                }
                        } // Конец Column внутри Scaffold content

                        AnimatedVisibility(
                                visible = showScrollToBottomButton && !isLoading && !isSelectionMode,
                                enter = fadeIn(animationSpec = tween(140)) + expandVertically(expandFrom = Alignment.Bottom),
                                exit = fadeOut(animationSpec = tween(120)) + shrinkVertically(shrinkTowards = Alignment.Bottom),
                                modifier =
                                        Modifier.align(Alignment.BottomEnd)
                                                .padding(
                                                        end = 14.dp,
                                                        bottom = if (isSystemAccount) 24.dp else 16.dp
                                                )
                        ) {
                                Box(
                                        modifier =
                                                Modifier.size(38.dp)
                                                        .clip(CircleShape)
                                                        .background(
                                                                if (isDarkTheme) Color(0xFF2D2E31)
                                                                else Color.White
                                                        )
                                                        .clickable(
                                                                indication = null,
                                                                interactionSource =
                                                                        remember {
                                                                                MutableInteractionSource()
                                                                        }
                                                        ) {
                                                                scope.launch {
                                                                        listState.animateScrollToItem(0)
                                                                        wasManualScroll = false
                                                                }
                                                        },
                                        contentAlignment = Alignment.Center
                                ) {
                                        Icon(
                                                imageVector = TablerIcons.ChevronDown,
                                                contentDescription = "Scroll to bottom",
                                                tint =
                                                        if (isDarkTheme) Color(0xFFF2F2F3)
                                                        else Color(0xFF2D3138),
                                                modifier = Modifier.size(21.dp)
                                        )
                                }
                        }

                        // 📎 Media Picker — new tab-based ChatAttachAlert (Telegram-style)
                        // Feature flag: set USE_NEW_ATTACH_ALERT to false to use old MediaPickerBottomSheet
                        val USE_NEW_ATTACH_ALERT = true
                        if (USE_NEW_ATTACH_ALERT) {
                                ChatAttachAlert(
                                        isVisible = showMediaPicker,
                                        onDismiss = { showMediaPicker = false },
                                        isDarkTheme = isDarkTheme,
                                        currentUserPublicKey = currentUserPublicKey,
                                        onMediaSelected = { selectedMedia, caption ->
                                                val imageUris =
                                                        selectedMedia.filter { !it.isVideo }.map { it.uri }
                                                val videoUris =
                                                        selectedMedia.filter { it.isVideo }.map { it.uri }
                                                if (imageUris.isNotEmpty() || videoUris.isNotEmpty()) {
                                                        showMediaPicker = false
                                                        inputFocusTrigger++
                                                        if (imageUris.isNotEmpty()) {
                                                                attachmentsViewModel.sendImageGroupFromUris(
                                                                        imageUris,
                                                                        caption
                                                                )
                                                        }
                                                        if (videoUris.isNotEmpty()) {
                                                                videoUris.forEach { uri ->
                                                                        attachmentsViewModel.sendVideoCircleFromUri(uri)
                                                                }
                                                        }
                                                }
                                        },
                                        onMediaSelectedWithCaption = { mediaItem, caption ->
                                                showMediaPicker = false
                                                inputFocusTrigger++
                                                if (mediaItem.isVideo) {
                                                        attachmentsViewModel.sendVideoCircleFromUri(mediaItem.uri)
                                                } else {
                                                        attachmentsViewModel.sendImageFromUri(mediaItem.uri, caption)
                                                }
                                        },
                                        onOpenCamera = {
                                                val imm =
                                                        context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                                                imm.hideSoftInputFromWindow(view.windowToken, 0)
                                                window?.let { win ->
                                                        androidx.core.view.WindowCompat.getInsetsController(win, view)
                                                                .hide(androidx.core.view.WindowInsetsCompat.Type.ime())
                                                }
                                                view.findFocus()?.clearFocus()
                                                (context as? Activity)?.currentFocus?.clearFocus()
                                                keyboardController?.hide()
                                                focusManager.clearFocus(force = true)
                                                showEmojiPicker = false
                                                showMediaPicker = false
                                                showInAppCamera = true
                                        },
                                        onOpenFilePicker = {
                                                filePickerLauncher.launch("*/*")
                                        },
                                        onGalleryFilesSelected = { uris ->
                                                showMediaPicker = false
                                                inputFocusTrigger++
                                                scope.launch {
                                                        for (uri in uris) {
                                                                val fileName = MediaUtils.getFileName(context, uri)
                                                                val fileSize = MediaUtils.getFileSize(context, uri)
                                                                if (fileSize > MediaUtils.MAX_FILE_SIZE_MB * 1024 * 1024) {
                                                                        android.widget.Toast.makeText(
                                                                                context,
                                                                                "File too large (max ${MediaUtils.MAX_FILE_SIZE_MB} MB)",
                                                                                android.widget.Toast.LENGTH_LONG
                                                                        ).show()
                                                                        continue
                                                                }
                                                                val base64 = MediaUtils.uriToBase64File(context, uri)
                                                                if (base64 != null) {
                                                                        attachmentsViewModel.sendFileMessage(base64, fileName, fileSize)
                                                                }
                                                        }
                                                }
                                        },
                                        onFileSelected = { uri, fileName, fileSize ->
                                                showMediaPicker = false
                                                inputFocusTrigger++
                                                scope.launch {
                                                        if (fileSize > MediaUtils.MAX_FILE_SIZE_MB * 1024 * 1024) {
                                                                android.widget.Toast.makeText(
                                                                        context,
                                                                        "File too large (max ${MediaUtils.MAX_FILE_SIZE_MB} MB)",
                                                                        android.widget.Toast.LENGTH_LONG
                                                                ).show()
                                                                return@launch
                                                        }
                                                        val base64 = MediaUtils.uriToBase64File(context, uri)
                                                        if (base64 != null) {
                                                                attachmentsViewModel.sendFileMessage(base64, fileName, fileSize)
                                                        }
                                                }
                                        },
                                        onAvatarClick = {
                                                attachmentsViewModel.sendAvatarMessage()
                                        },
                                        recipientName = user.title,
                                        onPhotoPreviewRequested = { uri, sourceThumb, galleryUris, initialIndex ->
                                                hideInputOverlays()
                                                showMediaPicker = false
                                                showContextMenu = false
                                                contextMenuMessage = null
                                                simplePickerPreviewSourceThumb = sourceThumb
                                                simplePickerPreviewCaption = ""
                                                simplePickerPreviewUri = uri
                                                val normalizedGallery =
                                                        if (galleryUris.isNotEmpty()) galleryUris else listOf(uri)
                                                simplePickerPreviewGalleryUris = normalizedGallery
                                                simplePickerPreviewInitialIndex =
                                                        initialIndex.coerceIn(
                                                                0,
                                                                (normalizedGallery.size - 1).coerceAtLeast(0)
                                                        )
                                        }
                                )
                        } else {
                                MediaPickerBottomSheet(
                                        isVisible = showMediaPicker,
                                        onDismiss = { showMediaPicker = false },
                                        isDarkTheme = isDarkTheme,
                                        currentUserPublicKey = currentUserPublicKey,
                                        onMediaSelected = { selectedMedia, caption ->
                                                val imageUris =
                                                        selectedMedia.filter { !it.isVideo }.map { it.uri }
                                                val videoUris =
                                                        selectedMedia.filter { it.isVideo }.map { it.uri }
                                                if (imageUris.isNotEmpty() || videoUris.isNotEmpty()) {
                                                        showMediaPicker = false
                                                        inputFocusTrigger++
                                                        if (imageUris.isNotEmpty()) {
                                                                attachmentsViewModel.sendImageGroupFromUris(
                                                                        imageUris,
                                                                        caption
                                                                )
                                                        }
                                                        if (videoUris.isNotEmpty()) {
                                                                videoUris.forEach { uri ->
                                                                        attachmentsViewModel.sendVideoCircleFromUri(uri)
                                                                }
                                                        }
                                                }
                                        },
                                        onMediaSelectedWithCaption = { mediaItem, caption ->
                                                showMediaPicker = false
                                                inputFocusTrigger++
                                                if (mediaItem.isVideo) {
                                                        attachmentsViewModel.sendVideoCircleFromUri(mediaItem.uri)
                                                } else {
                                                        attachmentsViewModel.sendImageFromUri(mediaItem.uri, caption)
                                                }
                                        },
                                        onOpenCamera = {
                                                val imm =
                                                        context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                                                imm.hideSoftInputFromWindow(view.windowToken, 0)
                                                window?.let { win ->
                                                        androidx.core.view.WindowCompat.getInsetsController(win, view)
                                                                .hide(androidx.core.view.WindowInsetsCompat.Type.ime())
                                                }
                                                view.findFocus()?.clearFocus()
                                                (context as? Activity)?.currentFocus?.clearFocus()
                                                keyboardController?.hide()
                                                focusManager.clearFocus(force = true)
                                                showEmojiPicker = false
                                                showMediaPicker = false
                                                showInAppCamera = true
                                        },
                                        onOpenFilePicker = {
                                                filePickerLauncher.launch("*/*")
                                        },
                                        onAvatarClick = {
                                                attachmentsViewModel.sendAvatarMessage()
                                        },
                                        recipientName = user.title,
                                        onPhotoPreviewRequested = { uri, sourceThumb, galleryUris, initialIndex ->
                                                hideInputOverlays()
                                                showMediaPicker = false
                                                showContextMenu = false
                                                contextMenuMessage = null
                                                simplePickerPreviewSourceThumb = sourceThumb
                                                simplePickerPreviewCaption = ""
                                                simplePickerPreviewUri = uri
                                                val normalizedGallery =
                                                        if (galleryUris.isNotEmpty()) galleryUris else listOf(uri)
                                                simplePickerPreviewGalleryUris = normalizedGallery
                                                simplePickerPreviewInitialIndex =
                                                        initialIndex.coerceIn(
                                                                0,
                                                                (normalizedGallery.size - 1).coerceAtLeast(0)
                                                        )
                                        }
                                )
                        }
        } // Закрытие Box wrapper для Scaffold content
        } // Закрытие Box

        if (showChatOpenMetricsDialog) {
                val metricsReport = remember(chatOpenMetrics) { buildChatOpenMetricsReport(chatOpenMetrics) }
                AlertDialog(
                        onDismissRequest = { showChatOpenMetricsDialog = false },
                        containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
                        title = {
                                Text(
                                        text = "Chat Open Metrics",
                                        fontWeight = FontWeight.Bold,
                                        color = textColor
                                )
                        },
                        text = {
                                SelectionContainer {
                                        Text(
                                                text = metricsReport,
                                                color = if (isDarkTheme) Color(0xFFD8D8D8) else Color(0xFF2F2F2F),
                                                fontFamily = FontFamily.Monospace,
                                                fontSize = 12.sp,
                                                lineHeight = 18.sp,
                                                modifier =
                                                        Modifier.fillMaxWidth()
                                                                .heightIn(max = 440.dp)
                                                                .verticalScroll(rememberScrollState())
                                        )
                                }
                        },
                        confirmButton = {
                                TextButton(onClick = { showChatOpenMetricsDialog = false }) {
                                        Text("Close", color = PrimaryBlue)
                                }
                        }
                )
        }

        // Диалог подтверждения удаления чата
        if (showDeleteConfirm) {
                val isLeaveGroupDialog = user.publicKey.startsWith("#group:")
                AlertDialog(
                        onDismissRequest = { showDeleteConfirm = false },
                        containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
                        title = {
                                Text(
                                        if (isLeaveGroupDialog) "Leave Group" else "Delete Chat",
                                        fontWeight = FontWeight.Bold,
                                        color = textColor
                                )
                        },
                        text = {
                                Text(
                                        if (isLeaveGroupDialog) {
                                                "Are you sure you want to leave this group?"
                                        } else {
                                                "Are you sure you want to delete this chat? This action cannot be undone."
                                        },
                                        color = secondaryTextColor
                                )
                        },
                        confirmButton = {
                                TextButton(
                                        onClick = {
                                                showDeleteConfirm = false
                                                scope.launch {
                                                        try {
                                                                if (isLeaveGroupDialog) {
                                                                        groupRepository
                                                                                .leaveGroup(
                                                                                        currentUserPublicKey,
                                                                                        user.publicKey
                                                                                )
                                                                } else {
                                                                        // Вычисляем правильный dialog_key
                                                                        val dialogKey =
                                                                                if (currentUserPublicKey <
                                                                                                user.publicKey
                                                                                ) {
                                                                                        "$currentUserPublicKey:${user.publicKey}"
                                                                                } else {
                                                                                        "${user.publicKey}:$currentUserPublicKey"
                                                                                }

                                                                        // 🗑️ Очищаем ВСЕ кэши сообщений
                                                                        messageRepository
                                                                                .clearDialogCache(
                                                                                        user.publicKey
                                                                                )
                                                                        ChatViewModel
                                                                                .clearCacheForOpponent(
                                                                                        user.publicKey
                                                                                )

                                                                        // Удаляем все сообщения из диалога
                                                                        database.messageDao()
                                                                                .deleteDialog(
                                                                                        account =
                                                                                                currentUserPublicKey,
                                                                                        dialogKey =
                                                                                                dialogKey
                                                                                )
                                                                        database.messageDao()
                                                                                .deleteMessagesBetweenUsers(
                                                                                        account =
                                                                                                currentUserPublicKey,
                                                                                        user1 =
                                                                                                user.publicKey,
                                                                                        user2 =
                                                                                                currentUserPublicKey
                                                                                )

                                                                        // Очищаем кеш диалога
                                                                        database.dialogDao()
                                                                                .deleteDialog(
                                                                                        account =
                                                                                                currentUserPublicKey,
                                                                                        opponentKey =
                                                                                                user.publicKey
                                                                                )
                                                                }
                                                        } catch (e: Exception) {
                                                                // Error deleting chat
                                                        }
                                                        hideKeyboardAndBack()
                                                }
                                        }
                                ) {
                                        Text(
                                                if (isLeaveGroupDialog) "Leave" else "Delete",
                                                color = Color(0xFFFF3B30)
                                        )
                                }
                        },
                        dismissButton = {
                                TextButton(onClick = { showDeleteConfirm = false }) {
                                        Text("Cancel", color = PrimaryBlue)
                                }
                        }
                )
        }

        // Диалог подтверждения блокировки
        if (showBlockConfirm) {
                AlertDialog(
                        onDismissRequest = { showBlockConfirm = false },
                        containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
                        title = {
                                Text(
                                        "Block ${user.title.ifEmpty { "User" }}",
                                        fontWeight = FontWeight.Bold,
                                        color = textColor
                                )
                        },
                        text = {
                                Text(
                                        "Are you sure you want to block this user? They won't be able to send you messages.",
                                        color = secondaryTextColor
                                )
                        },
                        confirmButton = {
                                TextButton(
                                        onClick = {
                                                showBlockConfirm = false
                                                scope.launch {
                                                        try {
                                                                database.blacklistDao()
                                                                        .blockUser(
                                                                                com.rosetta
                                                                                        .messenger
                                                                                        .database
                                                                                        .BlacklistEntity(
                                                                                                publicKey =
                                                                                                        user.publicKey,
                                                                                                account =
                                                                                                        currentUserPublicKey
                                                                                        )
                                                                        )
                                                        } catch (e: Exception) {
                                                                // Error blocking user
                                                        }
                                                }
                                        }
                                ) { Text("Block", color = Color(0xFFFF3B30)) }
                        },
                        dismissButton = {
                                TextButton(onClick = { showBlockConfirm = false }) {
                                        Text("Cancel", color = PrimaryBlue)
                                }
                        }
                )
        }

        // Диалог подтверждения разблокировки
        if (showUnblockConfirm) {
                AlertDialog(
                        onDismissRequest = { showUnblockConfirm = false },
                        containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
                        title = {
                                Text(
                                        "Unblock ${user.title.ifEmpty { "User" }}",
                                        fontWeight = FontWeight.Bold,
                                        color = textColor
                                )
                        },
                        text = {
                                Text(
                                        "Are you sure you want to unblock this user? They will be able to send you messages again.",
                                        color = secondaryTextColor
                                )
                        },
                        confirmButton = {
                                TextButton(
                                        onClick = {
                                                showUnblockConfirm = false
                                                scope.launch {
                                                        try {
                                                                database.blacklistDao()
                                                                        .unblockUser(
                                                                                publicKey =
                                                                                        user.publicKey,
                                                                                account =
                                                                                        currentUserPublicKey
                                                                        )
                                                        } catch (e: Exception) {
                                                                // Error unblocking user
                                                        }
                                                }
                                        }
                                ) { Text("Unblock", color = PrimaryBlue) }
                        },
                        dismissButton = {
                                TextButton(onClick = { showUnblockConfirm = false }) {
                                        Text("Cancel", color = Color(0xFF8E8E93))
                                }
                        }
                )
        }

        //  Forward Chat Picker BottomSheet
        if (showForwardPicker) {
                ForwardChatPickerBottomSheet(
                        dialogs = dialogsList,
                        isDarkTheme = isDarkTheme,
                        currentUserPublicKey = currentUserPublicKey,
                        avatarRepository = avatarRepository,
                        onDismiss = {
                                showForwardPicker = false
                                ForwardManager.clear()
                        },
                        onChatsSelected = { selectedDialogs ->
                                showForwardPicker = false
                                if (selectedDialogs.isEmpty()) {
                                        ForwardManager.clear()
                                        return@ForwardChatPickerBottomSheet
                                }

                                val forwardMessages = ForwardManager.consumeForwardMessages()
                                if (forwardMessages.isEmpty()) {
                                        ForwardManager.clear()
                                        return@ForwardChatPickerBottomSheet
                                }

                                // Desktop parity: если выбран один чат, не отправляем сразу.
                                // Открываем чат с forward-панелью, чтобы пользователь мог
                                // добавить подпись и отправить вручную.
                                if (selectedDialogs.size == 1) {
                                        val targetDialog = selectedDialogs.first()
                                        ForwardManager.setForwardMessages(
                                                forwardMessages,
                                                showPicker = false
                                        )
                                        ForwardManager.selectChat(targetDialog.opponentKey)

                                        if (targetDialog.opponentKey != user.publicKey) {
                                                val searchUser =
                                                        SearchUser(
                                                                title = targetDialog.opponentTitle,
                                                                username =
                                                                        targetDialog.opponentUsername,
                                                                publicKey = targetDialog.opponentKey,
                                                                verified = targetDialog.verified,
                                                                online = targetDialog.isOnline
                                                        )
                                                onNavigateToChat(searchUser)
                                        }
                                        return@ForwardChatPickerBottomSheet
                                }

                                ForwardManager.clear()

                                // Мультивыбор оставляем прямой отправкой как раньше.
                                selectedDialogs.forEach { dialog ->
                                        messagesViewModel.sendForwardDirectly(
                                                dialog.opponentKey,
                                                forwardMessages
                                        )
                                }

                                // Открываем первый выбранный чат (кроме текущего), чтобы сразу увидеть результат.
                                val primaryDialog = selectedDialogs.first()
                                if (primaryDialog.opponentKey != user.publicKey) {
                                        val searchUser =
                                                SearchUser(
                                                        title = primaryDialog.opponentTitle,
                                                        username = primaryDialog.opponentUsername,
                                                        publicKey = primaryDialog.opponentKey,
                                                        verified = primaryDialog.verified,
                                                        online = primaryDialog.isOnline
                                                )
                                        onNavigateToChat(searchUser)
                                }
                        }
                )
        }

} // Закрытие Scaffold content lambda

        simplePickerPreviewUri?.let { previewUri ->
                SimpleFullscreenPhotoOverlay(
                        imageUri = previewUri,
                        sourceThumbnail = simplePickerPreviewSourceThumb,
                        galleryImageUris = simplePickerPreviewGalleryUris,
                        initialGalleryIndex = simplePickerPreviewInitialIndex,
                        modifier = Modifier.fillMaxSize().zIndex(100f),
                        showCaptionInput = true,
                        caption = simplePickerPreviewCaption,
                        onCaptionChange = { simplePickerPreviewCaption = it },
                        isDarkTheme = isDarkTheme,
                        onSend = { editedUri, caption ->
                                attachmentsViewModel.sendImageFromUri(editedUri, caption)
                                showMediaPicker = false
                                simplePickerPreviewUri = null
                                simplePickerPreviewSourceThumb = null
                                simplePickerPreviewCaption = ""
                                simplePickerPreviewGalleryUris = emptyList()
                                simplePickerPreviewInitialIndex = 0
                                inputFocusTrigger++
                        },
                        onDismiss = {
                                simplePickerPreviewUri = null
                                simplePickerPreviewSourceThumb = null
                                simplePickerPreviewCaption = ""
                                simplePickerPreviewGalleryUris = emptyList()
                                simplePickerPreviewInitialIndex = 0
                                inputFocusTrigger++
                        }
                )
        }

        // � Image Viewer Overlay — FULLSCREEN поверх Scaffold
        if (showImageViewer && imageViewerImages.isNotEmpty()) {
                ImageViewerScreen(
                        images = imageViewerImages,
                        initialIndex = imageViewerInitialIndex,
                        privateKey = currentUserPrivateKey,
                        onDismiss = {
                                showImageViewer = false
                                imageViewerSourceBounds = null
                                imageViewerImages = emptyList()
                                onImageViewerChanged(false)
                        },
                        onClosingStart = {
                                // Сразу сбрасываем status bar при начале закрытия (до анимации)
                                SystemBarsStyleUtils.applyChatStatusBar(window, view, isDarkTheme)
                        },
                        isDarkTheme = isDarkTheme,
                        sourceBounds = imageViewerSourceBounds
                )
        }

        // �📷 In-App Camera — FULLSCREEN поверх Scaffold (вне content lambda)
        if (showInAppCamera) {
                InAppCameraScreen(
                        preferencesManager = preferencesManager,
                        onDismiss = { showInAppCamera = false },
                        onPhotoTaken = { photoUri ->
                                // После камеры открываем тот же fullscreen-редактор,
                                // что и для фото из галереи.
                                pendingCameraPhotoUri = photoUri
                                showInAppCamera = false
                        }
                )
        }

        // 📷 Image Editor для фото с камеры (с caption как в Telegram)
        pendingCameraPhotoUri?.let { uri ->
                SimpleFullscreenPhotoOverlay(
                        imageUri = uri,
                        modifier = Modifier.fillMaxSize().zIndex(100f),
                        showCaptionInput = true,
                        caption = pendingCameraPhotoCaption,
                        onCaptionChange = { pendingCameraPhotoCaption = it },
                        isDarkTheme = isDarkTheme,
                        onSend = { editedUri, caption ->
                                attachmentsViewModel.sendImageFromUri(editedUri, caption)
                                showMediaPicker = false
                                pendingCameraPhotoUri = null
                                pendingCameraPhotoCaption = ""
                                inputFocusTrigger++
                        },
                        onDismiss = {
                                pendingCameraPhotoUri = null
                                pendingCameraPhotoCaption = ""
                                inputFocusTrigger++
                        }
                )
        }

        // 📸 Multi-Image Editor для фото из галереи (со свайпом как в Telegram)
        if (pendingGalleryImages.isNotEmpty()) {
                MultiImageEditorScreen(
                        imageUris = pendingGalleryImages,
                        onDismiss = {
                                pendingGalleryImages = emptyList()
                                inputFocusTrigger++
                        },
                        onSendAll = { imagesWithCaptions ->
                                val imageUris = imagesWithCaptions.map { it.uri }
                                val groupCaption =
                                        imagesWithCaptions
                                                .firstOrNull { it.caption.isNotBlank() }
                                                ?.caption
                                                ?.trim()
                                                .orEmpty()

                                attachmentsViewModel.sendImageGroupFromUris(imageUris, groupCaption)
                                showMediaPicker = false
                        },
                        isDarkTheme = isDarkTheme,
                        recipientName = user.title
                )
        }

} // Закрытие outer Box
}

@Composable
private fun ChatInputBarSection(
        messagesViewModel: MessagesViewModel,
        typingViewModel: TypingViewModel,
        isSavedMessages: Boolean,
        onSend: () -> Unit,
        onSendVoiceMessage: (voiceHex: String, durationSec: Int, waves: List<Float>) -> Unit,
        isDarkTheme: Boolean,
        backgroundColor: Color,
        textColor: Color,
        secondaryTextColor: Color,
        replyMessages: List<ChatViewModel.ReplyMessage>,
        isForwardMode: Boolean,
        onCloseReply: () -> Unit,
        onShowForwardOptions: (List<ChatViewModel.ReplyMessage>) -> Unit,
        chatTitle: String,
        isBlocked: Boolean,
        showEmojiPicker: Boolean,
        onToggleEmojiPicker: (Boolean) -> Unit,
        coordinator: KeyboardTransitionCoordinator,
        displayReplyMessages: List<ChatViewModel.ReplyMessage>,
        onReplyClick: (String) -> Unit,
        onAttachClick: () -> Unit,
        myPublicKey: String,
        opponentPublicKey: String,
        myPrivateKey: String,
        isGroupChat: Boolean,
        mentionCandidates: List<MentionCandidate>,
        avatarRepository: AvatarRepository?,
        inputFocusTrigger: Int,
        suppressKeyboard: Boolean,
        hasNativeNavigationBar: Boolean
) {
        val inputText by messagesViewModel.inputText.collectAsState()

        MessageInputBar(
                value = inputText,
                onValueChange = {
                        messagesViewModel.updateInputText(it)
                        if (it.isNotEmpty() && !isSavedMessages) {
                                typingViewModel.sendTypingIndicator()
                        }
                },
                onSend = onSend,
                onSendVoiceMessage = onSendVoiceMessage,
                isDarkTheme = isDarkTheme,
                backgroundColor = backgroundColor,
                textColor = textColor,
                secondaryTextColor = secondaryTextColor,
                replyMessages = replyMessages,
                isForwardMode = isForwardMode,
                onCloseReply = onCloseReply,
                onShowForwardOptions = onShowForwardOptions,
                chatTitle = chatTitle,
                isBlocked = isBlocked,
                showEmojiPicker = showEmojiPicker,
                onToggleEmojiPicker = onToggleEmojiPicker,
                coordinator = coordinator,
                displayReplyMessages = displayReplyMessages,
                onReplyClick = onReplyClick,
                onAttachClick = onAttachClick,
                myPublicKey = myPublicKey,
                opponentPublicKey = opponentPublicKey,
                myPrivateKey = myPrivateKey,
                isGroupChat = isGroupChat,
                mentionCandidates = mentionCandidates,
                avatarRepository = avatarRepository,
                inputFocusTrigger = inputFocusTrigger,
                suppressKeyboard = suppressKeyboard,
                hasNativeNavigationBar = hasNativeNavigationBar
        )
}

@Composable
private fun GroupMembersSubtitleSkeleton() {
        val transition = rememberInfiniteTransition(label = "groupMembersSkeleton")
        val shimmerProgress by
                transition.animateFloat(
                        initialValue = -1f,
                        targetValue = 2f,
                        animationSpec =
                                infiniteRepeatable(
                                        animation = tween(1200, easing = LinearEasing),
                                        repeatMode = RepeatMode.Restart
                                ),
                        label = "groupMembersSkeletonShimmer"
                )

        BoxWithConstraints(
                modifier =
                        Modifier.padding(top = 2.dp)
                                .width(76.dp)
                                .height(12.dp)
        ) {
                val density = LocalDensity.current
                val widthPx = with(density) { maxWidth.toPx() }.coerceAtLeast(1f)
                val gradientWidthPx = with(density) { 44.dp.toPx() }
                val shimmerX = shimmerProgress * widthPx

                val shimmerBrush =
                        Brush.linearGradient(
                                colorStops =
                                        arrayOf(
                                                0f to Color.White.copy(alpha = 0.22f),
                                                0.5f to Color.White.copy(alpha = 0.56f),
                                                1f to Color.White.copy(alpha = 0.22f)
                                        ),
                                start = Offset(shimmerX - gradientWidthPx, 0f),
                                end = Offset(shimmerX, 0f)
                        )

                Box(
                        modifier =
                                Modifier.fillMaxSize()
                                        .clip(RoundedCornerShape(6.dp))
                                        .background(shimmerBrush)
                )
        }
}

@Composable
private fun ChatWallpaperBackground(
        wallpaperResId: Int,
        modifier: Modifier = Modifier
) {
        Image(
                painter = painterResource(id = wallpaperResId),
                contentDescription = "Chat wallpaper",
                modifier = modifier,
                contentScale = ContentScale.Crop
        )
}

private fun formatMetricDuration(valueMs: Long?): String =
        if (valueMs == null || valueMs < 0L) "n/a" else "${valueMs}ms"

private fun buildChatOpenMetricsReport(metrics: ChatViewModel.ChatOpenMetricsSnapshot): String {
        val chat = metrics.chat.ifBlank { "<empty>" }
        val logSection =
                if (metrics.eventLog.isEmpty()) {
                        "<empty>"
                } else {
                        metrics.eventLog.joinToString(separator = "\n")
                }
        return buildString {
                appendLine("chat=$chat")
                appendLine("messages=${metrics.messages}")
                appendLine("messagesWithDates=${metrics.messagesWithDates}")
                appendLine("isLoading=${metrics.isLoading}")
                appendLine("openDialog=${formatMetricDuration(metrics.openDialogMs)}")
                appendLine("firstMessages=${formatMetricDuration(metrics.firstMessagesMs)}")
                appendLine(
                        "messagesWithDatesReady=${formatMetricDuration(metrics.messagesWithDatesReadyMs)}"
                )
                appendLine("firstListLayout=${formatMetricDuration(metrics.firstListLayoutMs)}")
                appendLine("loadingFinished=${formatMetricDuration(metrics.loadingFinishedMs)}")
                appendLine()
                appendLine("Event Log")
                append(logSection)
        }
}
