package com.rosetta.messenger.ui.chats.attach

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.provider.Settings
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.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.*
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshotFlow
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.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.*
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupProperties
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.rosetta.messenger.ui.chats.components.ImageEditorScreen
import com.rosetta.messenger.ui.chats.components.PhotoPreviewWithCaptionScreen
import com.rosetta.messenger.ui.chats.components.SimpleFullscreenPhotoViewer
import com.rosetta.messenger.ui.chats.components.ThumbnailPosition
import com.rosetta.messenger.ui.components.OptimizedEmojiPicker
import com.rosetta.messenger.ui.components.KeyboardHeightProvider
import app.rosette.android.ui.keyboard.KeyboardTransitionCoordinator
import app.rosette.android.ui.keyboard.AnimatedKeyboardTransition
import app.rosette.android.ui.keyboard.rememberKeyboardTransitionCoordinator
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import com.rosetta.messenger.ui.utils.NavigationModeUtils
import kotlinx.coroutines.launch
import android.net.Uri
import android.util.Log
import android.view.View
import android.view.WindowManager
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

// ═══════════════════════════════════════════════════════════
// Debug log buffer — captures AttachAlert logs in-memory
// ═══════════════════════════════════════════════════════════
private object AttachAlertDebugLog {
    private val _entries = mutableListOf<String>()
    val entries: List<String> get() = _entries.toList()
    private val timeFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)

    fun log(tag: String, msg: String) {
        val time = timeFormat.format(Date())
        val entry = "$time [$tag] $msg"
        synchronized(_entries) {
            _entries.add(entry)
            if (_entries.size > 200) _entries.removeAt(0) // keep last 200
        }
        Log.d(tag, msg) // also send to Logcat
    }

    fun warn(tag: String, msg: String, t: Throwable? = null) {
        val time = timeFormat.format(Date())
        val entry = "$time [W/$tag] $msg${if (t != null) " | ${t.message}" else ""}"
        synchronized(_entries) {
            _entries.add(entry)
            if (_entries.size > 200) _entries.removeAt(0)
        }
        if (t != null) Log.w(tag, msg, t) else Log.w(tag, msg)
    }

    fun clear() = synchronized(_entries) { _entries.clear() }
}

/**
 * Telegram-style FLAG_ALT_FOCUSABLE_IM toggle on the Popup window.
 * Synchronous — no recomposition dependency.
 *
 * When [imeFocusable] is false, FLAG_ALT_FOCUSABLE_IM is set so the Popup
 * doesn't interact with IME (keyboard stays with the Activity behind).
 * When true, the flag is cleared so the Popup receives keyboard input.
 */
private fun updatePopupImeFocusable(rootView: View, imeFocusable: Boolean) {
    val params = rootView.layoutParams as? WindowManager.LayoutParams ?: run {
        AttachAlertDebugLog.warn("AttachAlert", "updatePopupImeFocusable: layoutParams is not WM.LayoutParams!")
        return
    }
    val wm = rootView.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    if (imeFocusable) {
        params.flags = params.flags and WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM.inv()
    } else {
        params.flags = params.flags or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
    }
    // Telegram approach: SOFT_INPUT_ADJUST_NOTHING on the Popup window itself.
    // This prevents the system from resizing or panning the Popup when keyboard
    // opens. The sheet stays at the same height — we handle keyboard space
    // internally via a bottom spacer (like Telegram's AdjustPanLayoutHelper).
    params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
    try {
        wm.updateViewLayout(rootView, params)
        AttachAlertDebugLog.log("AttachAlert", "updatePopupImeFocusable($imeFocusable) — flags updated OK")
    } catch (e: IllegalArgumentException) {
        AttachAlertDebugLog.warn("AttachAlert", "updatePopupImeFocusable: view not attached", e)
    }
}

/**
 * Telegram-style attach alert (media picker bottom sheet).
 *
 * Tab-based architecture with Photo, File, and Avatar tabs.
 * Uses Popup for rendering over the keyboard, ViewModel for state management,
 * and smooth animations for all transitions.
 *
 * Signature is backwards-compatible with the old MediaPickerBottomSheet.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChatAttachAlert(
    isVisible: Boolean,
    onDismiss: () -> Unit,
    isDarkTheme: Boolean,
    onMediaSelected: (List<MediaItem>, String) -> Unit,
    onMediaSelectedWithCaption: ((MediaItem, String) -> Unit)? = null,
    onOpenCamera: () -> Unit = {},
    onOpenFilePicker: () -> Unit = {},
    onGalleryFilesSelected: (List<android.net.Uri>) -> Unit = {},
    onFileSelected: (android.net.Uri, String, Long) -> Unit = { _, _, _ -> },
    onAvatarClick: () -> Unit = {},
    currentUserPublicKey: String = "",
    maxSelection: Int = 10,
    recipientName: String? = null,
    onPhotoPreviewRequested: ((Uri, ThumbnailPosition?, List<Uri>, Int) -> Unit)? = null,
    viewModel: AttachAlertViewModel = viewModel()
) {
    val context = LocalContext.current
    val hasNativeNavigationBar = remember(context) {
        NavigationModeUtils.hasNativeNavigationBar(context)
    }
    val density = LocalDensity.current
    val imeInsets = WindowInsets.ime
    val focusManager = LocalFocusManager.current

    val state by viewModel.uiState.collectAsState()

    // ═══════════════════════════════════════════════════════════
    // Local UI state (animation-related, not in ViewModel)
    // ═══════════════════════════════════════════════════════════

    var captionEditTextView by remember { mutableStateOf<com.rosetta.messenger.ui.components.AppleEmojiEditTextView?>(null) }
    var thumbnailPosition by remember { mutableStateOf<ThumbnailPosition?>(null) }
    var pendingPhotoUri by remember { mutableStateOf<Uri?>(null) }
    var previewPhotoUri by remember { mutableStateOf<Uri?>(null) }
    var photoCaption by remember { mutableStateOf("") }
    var isExpanded by remember { mutableStateOf(false) }
    var isClosing by remember { mutableStateOf(false) }
    var shouldShow by remember { mutableStateOf(false) }
    var closeAction by remember { mutableStateOf<(() -> Unit)?>(null) }
    var showDiscardSelectionDialog by remember { mutableStateOf(false) }
    var pendingDismissAfterConfirm by remember { mutableStateOf<(() -> Unit)?>(null) }
    var hadSelection by remember { mutableStateOf(false) }
    var isCaptionKeyboardVisible by remember { mutableStateOf(false) }
    var showEmojiPicker by remember { mutableStateOf(false) }
    val coordinator = rememberKeyboardTransitionCoordinator()
    var lastToggleTime by remember { mutableLongStateOf(0L) }
    val toggleCooldownMs = 500L
    var showAlbumMenu by remember { mutableStateOf(false) }
    var imeBottomInsetPx by remember { mutableIntStateOf(0) }

    // Keyboard-aware: Telegram FLAG_ALT_FOCUSABLE_IM pattern.
    // Popup is always focusable (for touch + back), but FLAG_ALT_FOCUSABLE_IM
    // prevents IME interaction until caption is tapped.
    var captionInputActive by remember { mutableStateOf(false) }
    var pendingCaptionFocus by remember { mutableStateOf(false) }
    var popupRootView by remember { mutableStateOf<View?>(null) }
    val activity = context as? Activity
    val savedSoftInputMode = remember { mutableIntStateOf(
        activity?.window?.attributes?.softInputMode
            ?: WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
    ) }

    // ═══════════════════════════════════════════════════════════
    // Keyboard helpers
    // ═══════════════════════════════════════════════════════════

    fun hideUnderlyingChatKeyboard() {
        focusManager.clearFocus(force = true)
        activity?.currentFocus?.clearFocus()
        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        val servedToken =
            activity?.currentFocus?.windowToken
                ?: activity?.window?.decorView?.findFocus()?.windowToken
                ?: activity?.window?.decorView?.windowToken
        servedToken?.let { token ->
            imm.hideSoftInputFromWindow(token, 0)
            imm.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS)
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            activity?.window?.insetsController?.hide(android.view.WindowInsets.Type.ime())
        }
    }

    fun hideKeyboard() {
        AttachAlertDebugLog.log("AttachAlert", "hideKeyboard() called, captionInputActive=$captionInputActive, shouldShow=$shouldShow, isClosing=$isClosing")
        pendingCaptionFocus = false
        captionInputActive = false
        showEmojiPicker = false
        coordinator.isEmojiVisible = false
        coordinator.isEmojiBoxVisible = false
        focusManager.clearFocus(force = true)
        captionEditTextView?.clearFocus()

        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        // Hide keyboard using the caption EditText's window token (Popup window)
        captionEditTextView?.windowToken?.let { token ->
            imm.hideSoftInputFromWindow(token, 0)
        }

        // Restore FLAG_ALT_FOCUSABLE_IM on Popup window (non-IME mode)
        popupRootView?.let { updatePopupImeFocusable(it, false) }
    }

    fun resetPickerCaptionInput() {
        viewModel.updateCaption("")
        captionEditTextView?.clearFocus()
    }

    // Telegram pattern: synchronously clear FLAG_ALT_FOCUSABLE_IM, defer focus to LaunchedEffect
    fun focusCaptionInput() {
        AttachAlertDebugLog.log("AttachAlert", "focusCaptionInput() called, popupRootView=${popupRootView != null}, editTextView=${captionEditTextView != null}")
        captionInputActive = true
        // Synchronously update Popup window flags (like Telegram's setFocusable(true))
        popupRootView?.let {
            updatePopupImeFocusable(it, true)
        } ?: AttachAlertDebugLog.warn("AttachAlert", "popupRootView is NULL — cannot update window flags!")
        pendingCaptionFocus = true
    }

    // Telegram-style emoji toggle using KeyboardTransitionCoordinator
    fun toggleEmojiPicker() {
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastToggleTime < toggleCooldownMs) return
        lastToggleTime = currentTime

        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

        if (coordinator.isEmojiVisible) {
            // EMOJI → KEYBOARD
            coordinator.requestShowKeyboard(
                showKeyboard = {
                    captionInputActive = true
                    popupRootView?.let { updatePopupImeFocusable(it, true) }
                    captionEditTextView?.let { editText ->
                        editText.requestFocus()
                        @Suppress("DEPRECATION")
                        imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
                    }
                },
                hideEmoji = { showEmojiPicker = false }
            )
        } else if (isCaptionKeyboardVisible || imeBottomInsetPx > 0) {
            // KEYBOARD → EMOJI
            coordinator.requestShowEmoji(
                hideKeyboard = {
                    captionEditTextView?.windowToken?.let { token ->
                        imm.hideSoftInputFromWindow(token, 0)
                    }
                },
                showEmoji = { showEmojiPicker = true }
            )
        } else {
            // Nothing visible → open emoji directly
            if (coordinator.emojiHeight == 0.dp) {
                // Use saved keyboard height minus nav bar (same as spacer)
                val savedPx = KeyboardHeightProvider.getSavedKeyboardHeight(context)
                val navBarPx = if (hasNativeNavigationBar) {
                    (context as? Activity)?.window?.decorView?.let { view ->
                        androidx.core.view.ViewCompat.getRootWindowInsets(view)
                            ?.getInsets(androidx.core.view.WindowInsetsCompat.Type.navigationBars())?.bottom ?: 0
                    } ?: 0
                } else {
                    0
                }
                val effectivePx = if (savedPx > 0) (savedPx - navBarPx).coerceAtLeast(0) else 0
                coordinator.emojiHeight = if (effectivePx > 0) {
                    with(density) { effectivePx.toDp() }
                } else {
                    260.dp
                }
            }
            coordinator.isEmojiBoxVisible = true
            coordinator.openEmojiOnly(
                showEmoji = { showEmojiPicker = true }
            )
        }
    }

    // ═══════════════════════════════════════════════════════════
    // Layout metrics
    // ═══════════════════════════════════════════════════════════

    val configuration = LocalConfiguration.current
    val screenHeight = configuration.screenHeightDp.dp
    val screenHeightPx = with(density) { screenHeight.toPx() }
    val navigationBarInsetPx = if (hasNativeNavigationBar) {
        WindowInsets.navigationBars.getBottom(density).toFloat()
    } else {
        0f
    }
    val statusBarInsetPx = WindowInsets.statusBars.getTop(density).toFloat()

    val collapsedHeightPx = screenHeightPx * 0.72f
    val expandedHeightPx =
        (screenHeightPx + statusBarInsetPx).coerceAtLeast(screenHeightPx * 0.95f)
    val minHeightPx = screenHeightPx * 0.2f
    val captionLiftEasing = CubicBezierEasing(0f, 0f, 0.2f, 1f)
    // Telegram uses DecelerateInterpolator for all showCommentTextView animations.
    // CubicBezier(0,0,0.2,1) approximates DecelerateInterpolator — unify to one easing.
    val telegramButtonsEasing = captionLiftEasing
    val collapsedStateHeightPx = collapsedHeightPx.coerceAtMost(expandedHeightPx)
    val selectionHeaderExtraPx = with(density) { 26.dp.toPx() }

    val captionBarProgress by animateFloatAsState(
        targetValue = if (state.selectedItemOrder.isNotEmpty()) 1f else 0f,
        animationSpec = tween(180, easing = captionLiftEasing),
        label = "caption_bar_progress"
    )

    val sheetHeightPx = remember { Animatable(collapsedHeightPx) }
    var dragSheetHeightPx by remember { mutableFloatStateOf(Float.NaN) }
    val interactiveSheetHeightPx by remember {
        derivedStateOf {
            if (dragSheetHeightPx.isNaN()) sheetHeightPx.value else dragSheetHeightPx
        }
    }
    val animationScope = rememberCoroutineScope()

    // ═══════════════════════════════════════════════════════════
    // Permission handling
    // ═══════════════════════════════════════════════════════════
    val mediaPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        arrayOf(
            Manifest.permission.READ_MEDIA_IMAGES,
            Manifest.permission.READ_MEDIA_VIDEO
        )
    } else {
        arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
    }
    var mediaPermissionRequestedOnce by rememberSaveable { mutableStateOf(false) }

    val permissionLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        val granted = mediaPermissions.all { permissions[it] == true }
        viewModel.setPermissionGranted(granted)
        if (granted) {
            viewModel.loadMedia(context)
        }
    }

    fun requestPermissions() {
        val deniedPermissions = mediaPermissions.filter {
            ContextCompat.checkSelfPermission(context, it) != PackageManager.PERMISSION_GRANTED
        }
        if (deniedPermissions.isEmpty()) {
            viewModel.setPermissionGranted(true)
            viewModel.loadMedia(context)
            return
        }

        val activity = context as? Activity
        val permanentlyDenied = activity != null &&
            mediaPermissionRequestedOnce &&
            deniedPermissions.all { permission ->
                !ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
            }

        if (permanentlyDenied) {
            val intent = Intent(
                Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                Uri.fromParts("package", context.packageName, null)
            ).apply {
                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            }
            context.startActivity(intent)
            return
        }

        mediaPermissionRequestedOnce = true
        permissionLauncher.launch(mediaPermissions)
    }

    // ═══════════════════════════════════════════════════════════
    // Animations
    // ═══════════════════════════════════════════════════════════

    val animatedOffset by animateFloatAsState(
        targetValue = if (shouldShow && !isClosing) 0f else 1f,
        animationSpec = if (isClosing) {
            tween(200, easing = FastOutSlowInEasing)
        } else {
            tween(280, easing = FastOutSlowInEasing)
        },
        finishedListener = {
            AttachAlertDebugLog.log("AttachAlert", "animatedOffset finished: isClosing=$isClosing, shouldShow=$shouldShow")
            if (isClosing) {
                AttachAlertDebugLog.log("AttachAlert", "CLOSE animation complete — hiding popup, restoring softInputMode")
                isClosing = false
                shouldShow = false
                isExpanded = false
                captionInputActive = false
                pendingCaptionFocus = false
                // Restore original softInputMode
                activity?.window?.setSoftInputMode(savedSoftInputMode.intValue)
                val action = closeAction
                closeAction = null
                animationScope.launch {
                    dragSheetHeightPx = Float.NaN
                    sheetHeightPx.snapTo(collapsedHeightPx)
                }
                action?.invoke()
                onDismiss()
            }
        },
        label = "sheet_slide"
    )

    val scrimAlpha by animateFloatAsState(
        targetValue = if (shouldShow && !isClosing) {
            val expandRange = (expandedHeightPx - collapsedStateHeightPx).coerceAtLeast(1f)
            val expandProgress = (interactiveSheetHeightPx - collapsedStateHeightPx) / expandRange
            0.25f + 0.15f * expandProgress.coerceIn(0f, 1f)
        } else 0f,
        animationSpec = tween(
            durationMillis = if (isClosing) 200 else 300,
            easing = FastOutSlowInEasing
        ),
        label = "scrim_fade"
    )

    // ═══════════════════════════════════════════════════════════
    // SoftInputMode safety net — restore on composable disposal
    // ═══════════════════════════════════════════════════════════

    DisposableEffect(Unit) {
        onDispose {
            activity?.window?.setSoftInputMode(savedSoftInputMode.intValue)
        }
    }

    // ═══════════════════════════════════════════════════════════
    // Sheet show/hide logic
    // ═══════════════════════════════════════════════════════════

    val showSheet = isVisible && pendingPhotoUri == null && previewPhotoUri == null
    val mediaGridState = rememberLazyGridState()

    // Check permissions and load media on show
    LaunchedEffect(isVisible) {
        if (isVisible) {
            viewModel.onShow()
            resetPickerCaptionInput()
            photoCaption = ""

            val hasPermission = mediaPermissions.all {
                ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
            }
            viewModel.setPermissionGranted(hasPermission)

            if (hasPermission) {
                viewModel.loadMedia(context)
            } else {
                requestPermissions()
            }
        } else if (shouldShow || isClosing) {
            // Parent hidden externally (e.g. app background): force-close immediately.
            // This guarantees camera preview does not stay active across app reopen.
            pendingCaptionFocus = false
            captionInputActive = false
            showEmojiPicker = false
            coordinator.isEmojiVisible = false
            coordinator.isEmojiBoxVisible = false
            isClosing = false
            shouldShow = false
            showAlbumMenu = false
        }
    }

    LaunchedEffect(showSheet) {
        if (showSheet && state.editingItem == null) {
            // Close chat keyboard before showing picker so no IME layer remains under it.
            hideUnderlyingChatKeyboard()
            kotlinx.coroutines.delay(16)
            // Telegram pattern: set ADJUST_NOTHING on Activity before showing popup
            // This prevents the system from resizing the layout when focus changes
            activity?.window?.let { win ->
                savedSoftInputMode.intValue = win.attributes.softInputMode
                win.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
            }
            captionInputActive = false
            // FLAG_ALT_FOCUSABLE_IM will be set via SideEffect inside the Popup
            shouldShow = true
            isClosing = false
            showAlbumMenu = false
            dragSheetHeightPx = Float.NaN
            sheetHeightPx.snapTo(collapsedStateHeightPx)
        }
    }

    // Grow sheet when items selected (collapsed only)
    LaunchedEffect(showSheet, state.selectedItemOrder.isNotEmpty(), isExpanded, isClosing, state.editingItem, dragSheetHeightPx) {
        if (showSheet && state.editingItem == null && !isClosing && !isExpanded && dragSheetHeightPx.isNaN()) {
            val targetHeight = if (state.selectedItemOrder.isNotEmpty()) {
                collapsedStateHeightPx + selectionHeaderExtraPx
            } else {
                collapsedStateHeightPx
            }
            if (kotlin.math.abs(interactiveSheetHeightPx - targetHeight) > 1f) {
                sheetHeightPx.stop()
                sheetHeightPx.animateTo(
                    targetHeight,
                    animationSpec = tween(180, easing = captionLiftEasing)
                )
            }
        }
    }

    // Track IME insets — only treat as "caption keyboard" when caption is active
    // (i.e., FLAG_ALT_FOCUSABLE_IM is cleared and keyboard belongs to caption, not chat)
    LaunchedEffect(showSheet, captionInputActive) {
        AttachAlertDebugLog.log("AttachAlert", "IME-tracker LaunchedEffect: showSheet=$showSheet, captionInputActive=$captionInputActive")
        if (showSheet) {
            snapshotFlow { imeInsets.getBottom(density) }
                .collect { imeBottom ->
                    val prevIme = imeBottomInsetPx
                    imeBottomInsetPx = imeBottom
                    val newCaptionKbVisible = imeBottom > 0 && captionInputActive
                    if (prevIme != imeBottom || isCaptionKeyboardVisible != newCaptionKbVisible) {
                        AttachAlertDebugLog.log("AttachAlert", "IME inset: $prevIme → $imeBottom, captionActive=$captionInputActive, captionKbVisible: $isCaptionKeyboardVisible → $newCaptionKbVisible, shouldShow=$shouldShow, isClosing=$isClosing")
                    }
                    isCaptionKeyboardVisible = newCaptionKbVisible
                }
        } else {
            imeBottomInsetPx = 0
            isCaptionKeyboardVisible = false
        }
    }

    // Deferred caption focus: FLAG_ALT_FOCUSABLE_IM was cleared synchronously via
    // updatePopupImeFocusable(), now we just need a small delay for the WindowManager
    // to process the flag change, then request focus + show keyboard.
    // Mirrors Telegram's makeFocusable() double-runOnUIThread pattern.
    LaunchedEffect(captionInputActive, pendingCaptionFocus) {
        if (captionInputActive && pendingCaptionFocus) {
            AttachAlertDebugLog.log("AttachAlert", "LaunchedEffect: starting deferred caption focus")
            kotlinx.coroutines.delay(80) // Wait for WindowManager to process flag change
            captionEditTextView?.let { view ->
                AttachAlertDebugLog.log("AttachAlert", "LaunchedEffect: view found, windowToken=${view.windowToken != null}, isAttached=${view.isAttachedToWindow}")
                view.post {
                    view.requestFocus()
                    AttachAlertDebugLog.log("AttachAlert", "LaunchedEffect: requestFocus() called, hasFocus=${view.hasFocus()}")
                    view.postDelayed({
                        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE)
                            as InputMethodManager
                        // Use SHOW_FORCED instead of SHOW_IMPLICIT — IMPLICIT can be ignored
                        // by Android 13+ when EditText didn't receive a native touch event.
                        val shown = imm.showSoftInput(view, InputMethodManager.SHOW_FORCED)
                        AttachAlertDebugLog.log("AttachAlert", "LaunchedEffect: showSoftInput(SHOW_FORCED) result=$shown")
                        if (!shown) {
                            // Fallback: try WindowInsetsController on Android 11+
                            AttachAlertDebugLog.log("AttachAlert", "LaunchedEffect: SHOW_FORCED failed, trying WindowInsetsController")
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                                view.windowInsetsController?.show(
                                    android.view.WindowInsets.Type.ime()
                                )
                            }
                        }
                    }, 60)
                }
            } ?: AttachAlertDebugLog.warn("AttachAlert", "LaunchedEffect: captionEditTextView is NULL!")
            pendingCaptionFocus = false // Reset AFTER focus attempt, not before
        }
    }

    // Scroll grid on album change
    LaunchedEffect(state.selectedAlbumId) {
        mediaGridState.scrollToItem(0)
    }

    // Clear keyboard when selection cleared
    LaunchedEffect(state.selectedItemOrder.size) {
        if (state.selectedItemOrder.isNotEmpty()) {
            hadSelection = true
        } else if (hadSelection) {
            hadSelection = false
            hideKeyboard()
        }
    }

    // Hide popup when editing
    LaunchedEffect(state.editingItem) {
        if (state.editingItem != null) {
            shouldShow = false
        }
    }

    // ═══════════════════════════════════════════════════════════
    // Close helpers
    // ═══════════════════════════════════════════════════════════

    fun animatedClose(afterClose: (() -> Unit)? = null) {
        AttachAlertDebugLog.log("AttachAlert", "animatedClose() called, isClosing=$isClosing, captionInputActive=$captionInputActive, imeBottomInsetPx=$imeBottomInsetPx")
        if (afterClose != null) closeAction = afterClose
        if (!isClosing) isClosing = true
    }

    fun requestClose(afterClose: (() -> Unit)? = null) {
        if (state.selectedItemOrder.isNotEmpty()) {
            pendingDismissAfterConfirm = afterClose
            showDiscardSelectionDialog = true
            return
        }
        animatedClose(afterClose)
    }

    fun snapToNearestState(velocity: Float = 0f) {
        animationScope.launch {
            val currentHeight = interactiveSheetHeightPx
            if (!dragSheetHeightPx.isNaN()) {
                sheetHeightPx.stop()
                sheetHeightPx.snapTo(currentHeight)
                dragSheetHeightPx = Float.NaN
            }
            val velocityThreshold = 180f
            val expandSnapThreshold =
                collapsedStateHeightPx + (expandedHeightPx - collapsedStateHeightPx) * 0.35f

            when {
                velocity > velocityThreshold && currentHeight < collapsedStateHeightPx -> requestClose()
                currentHeight < minHeightPx + 30 -> requestClose()
                velocity < -velocityThreshold -> {
                    isExpanded = true
                    sheetHeightPx.animateTo(
                        expandedHeightPx,
                        animationSpec = spring(
                            dampingRatio = Spring.DampingRatioLowBouncy,
                            stiffness = Spring.StiffnessMediumLow
                        )
                    )
                }
                velocity > velocityThreshold -> {
                    if (isExpanded) {
                        isExpanded = false
                        sheetHeightPx.animateTo(
                            collapsedStateHeightPx,
                            animationSpec = spring(
                                dampingRatio = Spring.DampingRatioLowBouncy,
                                stiffness = Spring.StiffnessMediumLow
                            )
                        )
                    } else requestClose()
                }
                currentHeight > expandSnapThreshold -> {
                    isExpanded = true
                    sheetHeightPx.animateTo(
                        expandedHeightPx,
                        animationSpec = spring(
                            dampingRatio = Spring.DampingRatioLowBouncy,
                            stiffness = Spring.StiffnessMediumLow
                        )
                    )
                }
                else -> {
                    isExpanded = false
                    sheetHeightPx.animateTo(
                        collapsedStateHeightPx,
                        animationSpec = spring(
                            dampingRatio = Spring.DampingRatioLowBouncy,
                            stiffness = Spring.StiffnessMediumLow
                        )
                    )
                }
            }
        }
    }

    // ═══════════════════════════════════════════════════════════
    // System bars
    // ═══════════════════════════════════════════════════════════

    val view = LocalView.current
    val insetsController = remember(view) {
        val window = (view.context as? Activity)?.window
        window?.let { WindowCompat.getInsetsController(it, view) }
    }

    val isPickerFullScreen by remember {
        derivedStateOf {
            val expandRange = (expandedHeightPx - collapsedStateHeightPx).coerceAtLeast(1f)
            val progress = ((interactiveSheetHeightPx - collapsedStateHeightPx) / expandRange).coerceIn(0f, 1f)
            progress > 0.9f
        }
    }

    DisposableEffect(shouldShow) {
        if (shouldShow && !view.isInEditMode) {
            val window = (view.context as? Activity)?.window
            val origStatusBarColor = window?.statusBarColor ?: 0
            val origNavBarColor = window?.navigationBarColor ?: 0
            val origLightNav = insetsController?.isAppearanceLightNavigationBars ?: true
            val origLightStatus = insetsController?.isAppearanceLightStatusBars ?: false
            val origContrastEnforced =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    window?.isNavigationBarContrastEnforced
                } else {
                    null
                }
            onDispose {
                window?.statusBarColor = origStatusBarColor
                window?.navigationBarColor = origNavBarColor
                insetsController?.isAppearanceLightNavigationBars = origLightNav
                insetsController?.isAppearanceLightStatusBars = origLightStatus
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && origContrastEnforced != null) {
                    window?.isNavigationBarContrastEnforced = origContrastEnforced
                }
            }
        } else {
            onDispose { }
        }
    }

    LaunchedEffect(
        shouldShow,
        state.editingItem,
        isPickerFullScreen,
        isDarkTheme,
        hasNativeNavigationBar
    ) {
        if (!shouldShow || state.editingItem != null) return@LaunchedEffect
        val window = (view.context as? Activity)?.window ?: return@LaunchedEffect
        val dark = isDarkTheme
        val fullScreen = isPickerFullScreen

        if (hasNativeNavigationBar) {
            val navBaseColor = if (dark) 0xFF1C1C1E.toInt() else 0xFFFFFFFF.toInt()
            window.navigationBarColor = navBaseColor
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                window.isNavigationBarContrastEnforced = true
            }
            insetsController?.isAppearanceLightNavigationBars = !dark
        } else {
            window.navigationBarColor = android.graphics.Color.TRANSPARENT
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                window.isNavigationBarContrastEnforced = false
            }
            insetsController?.isAppearanceLightNavigationBars = !dark
        }

        // Telegram-like natural dim: status-bar tint follows the same scrim alpha
        // as the popup overlay, so top area and content overlay always match.
        if (fullScreen) {
            window.statusBarColor = if (dark) 0xFF1C1C1E.toInt() else 0xFFFFFFFF.toInt()
            insetsController?.isAppearanceLightStatusBars = false
        } else {
            insetsController?.isAppearanceLightStatusBars = false
            var lastAppliedAlpha = -1
            snapshotFlow { (scrimAlpha * 255f).toInt().coerceIn(0, 255) }
                .collect { alpha ->
                    if (alpha != lastAppliedAlpha) {
                        lastAppliedAlpha = alpha
                        window.statusBarColor = android.graphics.Color.argb(alpha, 0, 0, 0)
                    }
                }
        }
    }

    // ═══════════════════════════════════════════════════════════
    // Colors
    // ═══════════════════════════════════════════════════════════

    val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
    val textColor = if (isDarkTheme) Color.White else Color.Black
    val secondaryTextColor = Color(0xFF8E8E93)

    // ═══════════════════════════════════════════════════════════
    // POPUP RENDERING
    // ═══════════════════════════════════════════════════════════

    if (shouldShow && state.editingItem == null) {
        Popup(
            alignment = Alignment.TopStart,
            onDismissRequest = {
                // Popup is always focusable, so it handles Back presses.
                // Priority: hide keyboard → collapse → close
                AttachAlertDebugLog.log("AttachAlert", "onDismissRequest: captionInputActive=$captionInputActive, isExpanded=$isExpanded, isClosing=$isClosing")
                when {
                    captionInputActive -> hideKeyboard()
                    isExpanded -> {
                        animationScope.launch {
                            isExpanded = false
                            sheetHeightPx.animateTo(
                                collapsedStateHeightPx,
                                animationSpec = tween(250, easing = FastOutSlowInEasing)
                            )
                        }
                    }
                    else -> requestClose()
                }
            },
            properties = PopupProperties(
                focusable = true,              // Always focusable (touch + back)
                dismissOnBackPress = true,     // Always handle back
                dismissOnClickOutside = false
            )
        ) {
            // Capture popup root view for FLAG_ALT_FOCUSABLE_IM management
            val localPopupView = LocalView.current
            SideEffect {
                popupRootView = localPopupView.rootView
            }

            // Set FLAG_ALT_FOCUSABLE_IM on first show (non-IME mode)
            LaunchedEffect(Unit) {
                kotlinx.coroutines.delay(16) // Wait for view to be added to WindowManager
                AttachAlertDebugLog.log("AttachAlert", "Popup first-show: setting FLAG_ALT_FOCUSABLE_IM (non-IME mode)")
                popupRootView?.let { updatePopupImeFocusable(it, false) }
            }

            // Ensure FLAG_ALT_FOCUSABLE_IM is correct after any recomposition
            // (Compose might reset window flags)
            SideEffect {
                popupRootView?.let { rootView ->
                    val params = rootView.layoutParams as? WindowManager.LayoutParams
                        ?: return@SideEffect
                    val shouldHaveAltFlag = !captionInputActive
                    val hasAltFlag = (params.flags and
                        WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
                    if (shouldHaveAltFlag != hasAltFlag) {
                        updatePopupImeFocusable(rootView, captionInputActive)
                    }
                }
            }

            // Telegram approach: the sheet height NEVER changes for keyboard.
            // We compute the keyboard height only for a bottom Spacer inside the Column.
            val keyboardInsetPx =
                (imeBottomInsetPx.toFloat() - navigationBarInsetPx).coerceAtLeast(0f)
            val keyboardSpacerPxRaw =
                if (state.selectedItemOrder.isNotEmpty() && captionInputActive && !isClosing) keyboardInsetPx else 0f

            // Track whether emoji box was previously visible to detect the moment it disappears
            val prevEmojiBoxVisible = remember { mutableStateOf(false) }
            val emojiBoxCurrentlyVisible = coordinator.isEmojiBoxVisible
            val justDismissedEmoji = prevEmojiBoxVisible.value && !emojiBoxCurrentlyVisible

            // Update tracking ASAP
            SideEffect { prevEmojiBoxVisible.value = emojiBoxCurrentlyVisible }

            // Smooth spring animation on the spacer for a fluid keyboard transition
            // BUT: snap instantly when emoji box disappears (to avoid input dropping)
            val keyboardSpacerAnim = remember { Animatable(0f) }
            LaunchedEffect(keyboardSpacerPxRaw, emojiBoxCurrentlyVisible, justDismissedEmoji) {
                if (justDismissedEmoji) {
                    // Emoji box just disappeared → snap spacer to current keyboard height
                    keyboardSpacerAnim.snapTo(keyboardSpacerPxRaw)
                } else if (!emojiBoxCurrentlyVisible) {
                    // Normal case: animate with spring
                    keyboardSpacerAnim.animateTo(
                        keyboardSpacerPxRaw,
                        animationSpec = spring(
                            dampingRatio = Spring.DampingRatioNoBouncy,
                            stiffness = 400f
                        )
                    )
                }
                // When emoji box is visible, spacer is not rendered—no animation needed
            }
            val keyboardSpacerPx = keyboardSpacerAnim.value
            val keyboardSpacerDp = with(density) { keyboardSpacerPx.toDp() }

            // Feed coordinator the same nav-bar-subtracted keyboard height as the spacer.
            // This way emoji picker height == spacer height == exact keyboard visual area.
            LaunchedEffect(Unit) {
                snapshotFlow {
                    with(density) {
                        (imeBottomInsetPx.toFloat() - navigationBarInsetPx).coerceAtLeast(0f).toDp()
                    }
                }.collect { keyboardHeightDp ->
                    coordinator.updateKeyboardHeight(keyboardHeightDp)
                    if (keyboardHeightDp > 100.dp) {
                        coordinator.syncHeights()
                    }
                }
            }

            // Compute bottom padding for send button: emoji panel height or keyboard spacer
            val bottomInputPadding = if (coordinator.isEmojiBoxVisible) {
                coordinator.emojiHeight
            } else keyboardSpacerDp

            // When keyboard or emoji is open, nav bar is behind — don't pad for it
            val navInsetPxForSheet = if (keyboardSpacerPx > 0f || coordinator.isEmojiBoxVisible) 0f
                                     else navigationBarInsetPx
            val navInsetDpForSheet = with(density) { navInsetPxForSheet.toDp() }

            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Black.copy(alpha = scrimAlpha))
                    .clickable(
                        interactionSource = remember { MutableInteractionSource() },
                        indication = null
                    ) { requestClose() },
                contentAlignment = Alignment.BottomCenter
            ) {
                // Sheet height stays constant — keyboard space is handled by
                // internal Spacer, not by shrinking the container (Telegram approach).
                val visibleSheetHeightPx = (interactiveSheetHeightPx + navInsetPxForSheet).coerceAtLeast(minHeightPx)
                val currentHeightDp = with(density) { visibleSheetHeightPx.toDp() }
                val slideOffset = (visibleSheetHeightPx * animatedOffset).toInt()
                val expandProgress =
                    ((interactiveSheetHeightPx - collapsedStateHeightPx) /
                            (expandedHeightPx - collapsedStateHeightPx).coerceAtLeast(1f))
                        .coerceIn(0f, 1f)
                val topCornerRadius by animateDpAsState(
                    targetValue = 16.dp * (1f - expandProgress),
                    animationSpec = tween(180, easing = FastOutSlowInEasing),
                    label = "sheet_top_corner_radius"
                )
                val handleAlpha by animateFloatAsState(
                    targetValue = (1f - expandProgress * 1.35f).coerceIn(0f, 1f),
                    animationSpec = tween(160, easing = FastOutSlowInEasing),
                    label = "sheet_handle_alpha"
                )
                val fullScreenHeaderProgress by animateFloatAsState(
                    targetValue = if (expandProgress > 0.94f) 1f else 0f,
                    animationSpec = tween(170, easing = FastOutSlowInEasing),
                    label = "gallery_fullscreen_header_progress"
                )
                val collapsedHeaderAlpha = (1f - fullScreenHeaderProgress).coerceIn(0f, 1f)
                val selectedCount = state.selectedCount

                var lastDragVelocity by remember { mutableFloatStateOf(0f) }

                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(currentHeightDp)
                        .offset { IntOffset(0, slideOffset) }
                        .graphicsLayer {
                            val scale = 0.95f + 0.05f * (1f - animatedOffset)
                            scaleX = scale
                            scaleY = scale
                            alpha = 0.9f + 0.1f * (1f - animatedOffset)
                        }
                        .clip(RoundedCornerShape(topStart = topCornerRadius, topEnd = topCornerRadius))
                        .background(backgroundColor)
                        .clickable(
                            interactionSource = remember { MutableInteractionSource() },
                            indication = null
                        ) { /* Prevent click through */ }
                        .pointerInput(Unit) {
                            detectVerticalDragGestures(
                                onDragStart = {
                                    animationScope.launch { sheetHeightPx.stop() }
                                    dragSheetHeightPx = interactiveSheetHeightPx
                                },
                                onDragCancel = {
                                    snapToNearestState()
                                    lastDragVelocity = 0f
                                },
                                onDragEnd = {
                                    snapToNearestState(lastDragVelocity)
                                    lastDragVelocity = 0f
                                },
                                onVerticalDrag = { change, dragAmount ->
                                    change.consume()
                                    val adjustedDragAmount =
                                        if (dragAmount < 0f) dragAmount * 1.25f else dragAmount * 0.9f
                                    lastDragVelocity = adjustedDragAmount * 1.8f
                                    val newHeight = (interactiveSheetHeightPx - adjustedDragAmount)
                                        .coerceIn(minHeightPx, expandedHeightPx)
                                    dragSheetHeightPx = newHeight
                                }
                            )
                        }
                ) {
                    Column(modifier = Modifier.fillMaxSize()) {
                        // ── Header ──

                        val statusBarTopPadding = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
                        val currentAlbumTitle =
                            if (state.visibleAlbum?.isAllMedia != false) "Gallery"
                            else state.visibleAlbum?.name ?: "Gallery"
                        val selectionHeaderText = viewModel.selectionHeaderText()

                        val selectionHeaderHeightPx = if (!isExpanded) {
                            (interactiveSheetHeightPx - collapsedStateHeightPx)
                                .coerceIn(0f, selectionHeaderExtraPx)
                        } else 0f
                        val selectionHeaderHeightDp = with(density) { selectionHeaderHeightPx.toDp() }
                        val selectionHeaderAlpha =
                            (selectionHeaderHeightPx / selectionHeaderExtraPx).coerceIn(0f, 1f)

                        CollapsedHeader(
                            selectionHeaderText = selectionHeaderText,
                            selectionHeaderHeightDp = selectionHeaderHeightDp,
                            selectionHeaderAlpha = selectionHeaderAlpha,
                            handleAlpha = handleAlpha,
                            collapsedHeaderAlpha = collapsedHeaderAlpha,
                            textColor = textColor,
                            secondaryTextColor = secondaryTextColor
                        )

                        ExpandedHeader(
                            fullScreenHeaderProgress = fullScreenHeaderProgress,
                            statusBarTopPadding = statusBarTopPadding,
                            selectedCount = selectedCount,
                            currentAlbumTitle = currentAlbumTitle,
                            canSwitchAlbums = state.canSwitchAlbums,
                            showAlbumMenu = showAlbumMenu,
                            onShowAlbumMenu = { showAlbumMenu = true },
                            onDismissAlbumMenu = { showAlbumMenu = false },
                            albums = state.albums,
                            selectedAlbumId = state.selectedAlbumId,
                            onAlbumSelected = { viewModel.selectAlbum(it) },
                            onClose = { requestClose() },
                            isDarkTheme = isDarkTheme,
                            textColor = textColor,
                            secondaryTextColor = secondaryTextColor,
                            density = density
                        )

                        // ── Tab Content with AnimatedContent ──

                        AnimatedContent(
                            targetState = state.currentTab,
                            transitionSpec = {
                                val dir = if (targetState.ordinal > initialState.ordinal) 1 else -1
                                (slideInHorizontally { dir * it / 4 } + fadeIn(tween(200))) togetherWith
                                        (slideOutHorizontally { -dir * it / 4 } + fadeOut(tween(150)))
                            },
                            modifier = Modifier.weight(1f),
                            label = "tab_content"
                        ) { tab ->
                            when (tab) {
                                AttachAlertTab.PHOTO -> AttachAlertPhotoLayout(
                                    state = state,
                                    gridState = mediaGridState,
                                    cameraEnabled = !isClosing,
                                    onCameraClick = {
                                        requestClose {
                                            hideKeyboard()
                                            onOpenCamera()
                                        }
                                    },
                                    onItemClick = { item, position ->
                                        if (!item.isVideo) {
                                            hideKeyboard()
                                            if (onPhotoPreviewRequested != null) {
                                                val photoItems = state.visibleMediaItems.filter { !it.isVideo }
                                                val photoUris = photoItems.map { it.uri }
                                                val currentIndex =
                                                    photoItems.indexOfFirst { it.id == item.id }
                                                        .takeIf { it >= 0 }
                                                        ?: photoUris.indexOf(item.uri).coerceAtLeast(0)
                                                onPhotoPreviewRequested(item.uri, position, photoUris, currentIndex)
                                            } else {
                                                thumbnailPosition = position
                                                viewModel.setEditingItem(item)
                                            }
                                        } else {
                                            viewModel.toggleSelection(item.id, maxSelection)
                                        }
                                    },
                                    onItemCheckClick = { item ->
                                        viewModel.toggleSelection(item.id, maxSelection)
                                    },
                                    onItemLongClick = { item ->
                                        viewModel.toggleSelection(item.id, maxSelection)
                                    },
                                    onRequestPermission = { requestPermissions() },
                                    isDarkTheme = isDarkTheme,
                                    modifier = Modifier.fillMaxSize()
                                )
                                AttachAlertTab.FILE -> AttachAlertFileLayout(
                                    onFileSelected = { uri, name, size ->
                                        requestClose { onFileSelected(uri, name, size) }
                                    },
                                    onGalleryFilesSelected = { uris ->
                                        requestClose { onGalleryFilesSelected(uris) }
                                    },
                                    onOpenSystemPicker = {
                                        requestClose { onOpenFilePicker() }
                                    },
                                    galleryMediaItems = state.mediaItems,
                                    isDarkTheme = isDarkTheme,
                                    modifier = Modifier.fillMaxSize()
                                )
                                AttachAlertTab.AVATAR -> AttachAlertAvatarLayout(
                                    onAvatarClick = {
                                        requestClose { onAvatarClick() }
                                    },
                                    isDarkTheme = isDarkTheme,
                                    modifier = Modifier.fillMaxSize()
                                )
                            }
                        }

                        // ── Tab Bar (slides out when items selected) ──

                        val bottomButtonsVisible = state.selectedItemOrder.isEmpty() && !isCaptionKeyboardVisible
                        val bottomButtonsHideProgress by animateFloatAsState(
                            targetValue = if (bottomButtonsVisible) 0f else 1f,
                            animationSpec = tween(180, easing = telegramButtonsEasing),
                            label = "bottom_buttons_hide_progress"
                        )
                        val bottomButtonsFullHeight = 108.dp
                        val bottomButtonsHeight = bottomButtonsFullHeight * (1f - bottomButtonsHideProgress)
                        Box(
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(bottomButtonsHeight)
                                .graphicsLayer { clip = true }
                        ) {
                            if (bottomButtonsHideProgress < 0.999f) {
                                val rowOffsetPx =
                                    with(density) { bottomButtonsFullHeight.toPx() * bottomButtonsHideProgress }
                                AttachTabBar(
                                    currentTab = state.currentTab,
                                    onTabSelected = { viewModel.selectTab(it) },
                                    isDarkTheme = isDarkTheme,
                                    modifier = Modifier
                                        .fillMaxWidth()
                                        .graphicsLayer {
                                            translationY = rowOffsetPx
                                            alpha = 1f - bottomButtonsHideProgress
                                        }
                                )
                            }
                        }

                        // ── Caption Bar ──

                        CaptionBar(
                            captionBarProgress = captionBarProgress,
                            captionText = state.captionText,
                            onCaptionChange = { viewModel.updateCaption(it) },
                            isCaptionKeyboardVisible = isCaptionKeyboardVisible,
                            isEmojiVisible = showEmojiPicker,
                            onToggleKeyboard = { toggleEmojiPicker() },
                            onFocusCaption = { focusCaptionInput() },
                            onViewCreated = { captionEditTextView = it },
                            isDarkTheme = isDarkTheme,
                            density = density
                        )

                        // ── Emoji Picker via AnimatedKeyboardTransition ──
                        AnimatedKeyboardTransition(
                            coordinator = coordinator,
                            showEmojiPicker = showEmojiPicker
                        ) {
                            OptimizedEmojiPicker(
                                isVisible = true,
                                isDarkTheme = isDarkTheme,
                                onEmojiSelected = { emoji ->
                                    viewModel.updateCaption(state.captionText + emoji)
                                    captionEditTextView?.let { editText ->
                                        val newText = state.captionText + emoji
                                        editText.setText(newText)
                                        editText.setSelection(newText.length)
                                    }
                                },
                                onClose = { toggleEmojiPicker() },
                                modifier = Modifier.fillMaxSize()
                            )
                        }

                        // ── Keyboard spacer (Telegram approach, only when emoji box not shown) ──
                        if (!coordinator.isEmojiBoxVisible) {
                            Spacer(modifier = Modifier.height(keyboardSpacerDp))
                        }
                        if (navInsetDpForSheet > 0.dp) {
                            Spacer(modifier = Modifier.height(navInsetDpForSheet))
                        }
                    } // end Column

                    // ── Floating Send Button ──

                    val sendButtonVisible = state.selectedItemOrder.isNotEmpty()
                    val sendButtonProgress by animateFloatAsState(
                        targetValue = if (sendButtonVisible) 1f else 0f,
                        animationSpec = tween(180, easing = captionLiftEasing),
                        label = "send_button_progress"
                    )
                    FloatingSendButton(
                        selectedCount = state.selectedCount,
                        sendButtonProgress = sendButtonProgress,
                        backgroundColor = backgroundColor,
                        onSend = {
                            val selected = viewModel.resolveSelectedMedia()
                            val caption = state.captionText.trim()
                            resetPickerCaptionInput()
                            onMediaSelected(selected, caption)
                            animatedClose()
                        },
                        modifier = Modifier
                            .align(Alignment.BottomEnd)
                            .padding(bottom = bottomInputPadding + navInsetDpForSheet)
                    )

                } // end Box sheet container
            }
        }
    }

    // ═══════════════════════════════════════════════════════════
    // Discard dialog
    // ═══════════════════════════════════════════════════════════

    if (showDiscardSelectionDialog) {
        AlertDialog(
            onDismissRequest = {
                showDiscardSelectionDialog = false
                pendingDismissAfterConfirm = null
            },
            containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
            title = {
                Text(
                    text = "Discard selection?",
                    fontWeight = FontWeight.SemiBold,
                    color = textColor
                )
            },
            text = {
                Text(
                    text = "Selected photos will be removed.",
                    color = secondaryTextColor,
                    fontSize = 15.sp
                )
            },
            dismissButton = {
                TextButton(onClick = {
                    showDiscardSelectionDialog = false
                    pendingDismissAfterConfirm = null
                }) {
                    Text("Cancel", color = PrimaryBlue)
                }
            },
            confirmButton = {
                TextButton(onClick = {
                    val afterClose = pendingDismissAfterConfirm
                    showDiscardSelectionDialog = false
                    pendingDismissAfterConfirm = null
                    viewModel.clearSelection()
                    resetPickerCaptionInput()
                    animatedClose(afterClose)
                }) {
                    Text("Discard", color = Color(0xFFFF3B30))
                }
            }
        )
    }

    // ═══════════════════════════════════════════════════════════
    // Image Editor overlays (gallery photo + camera photo)
    // ═══════════════════════════════════════════════════════════

    state.editingItem?.let { item ->
        val galleryPhotoItems = state.visibleMediaItems.filter { !it.isVideo }
        val galleryPhotoUris = galleryPhotoItems.map { it.uri }
        val initialGalleryIndex =
            galleryPhotoItems.indexOfFirst { it.id == item.id }
                .takeIf { it >= 0 }
                ?: galleryPhotoUris.indexOf(item.uri).coerceAtLeast(0)
        SimpleFullscreenPhotoViewer(
            imageUri = item.uri,
            sourceThumbnail = thumbnailPosition,
            galleryImageUris = galleryPhotoUris,
            initialGalleryIndex = initialGalleryIndex,
            onDismiss = {
                viewModel.setEditingItem(null)
                thumbnailPosition = null
                shouldShow = true
            }
        )
    }

    pendingPhotoUri?.let { uri ->
        ImageEditorScreen(
            imageUri = uri,
            onDismiss = { pendingPhotoUri = null },
            onSave = { editedUri ->
                pendingPhotoUri = null
                if (onMediaSelectedWithCaption == null) {
                    previewPhotoUri = editedUri
                } else {
                    val mediaItem = MediaItem(
                        id = System.currentTimeMillis(),
                        uri = editedUri,
                        mimeType = "image/png",
                        dateModified = System.currentTimeMillis()
                    )
                    onMediaSelected(listOf(mediaItem), "")
                    onDismiss()
                }
            },
            onSaveWithCaption = if (onMediaSelectedWithCaption != null) { editedUri, caption ->
                pendingPhotoUri = null
                val mediaItem = MediaItem(
                    id = System.currentTimeMillis(),
                    uri = editedUri,
                    mimeType = "image/png",
                    dateModified = System.currentTimeMillis()
                )
                onMediaSelectedWithCaption(mediaItem, caption)
                onDismiss()
            } else null,
            isDarkTheme = isDarkTheme,
            showCaptionInput = onMediaSelectedWithCaption != null,
            recipientName = recipientName
        )
    }

    previewPhotoUri?.let { uri ->
        PhotoPreviewWithCaptionScreen(
            imageUri = uri,
            caption = photoCaption,
            onCaptionChange = { photoCaption = it },
            onSend = {
                val item = MediaItem(
                    id = System.currentTimeMillis(),
                    uri = uri,
                    mimeType = "image/png",
                    dateModified = System.currentTimeMillis()
                )
                onMediaSelected(listOf(item), "")
                previewPhotoUri = null
                photoCaption = ""
                onDismiss()
            },
            onDismiss = {
                previewPhotoUri = null
                photoCaption = ""
            },
            isDarkTheme = isDarkTheme
        )
    }
}
