package com.rosetta.messenger.ui.chats.components

import android.app.Activity
import android.content.Context
import android.graphics.Color as AndroidColor
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.DialogWindowProvider
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import coil.compose.AsyncImage
import coil.imageLoader
import coil.request.ImageRequest
import app.rosette.android.ui.keyboard.AnimatedKeyboardTransition
import app.rosette.android.ui.keyboard.rememberKeyboardTransitionCoordinator
import com.rosetta.messenger.ui.components.AppleEmojiEditTextView
import com.rosetta.messenger.ui.components.AppleEmojiTextField
import com.rosetta.messenger.ui.components.KeyboardHeightProvider
import com.rosetta.messenger.ui.components.OptimizedEmojiPicker
import com.rosetta.messenger.ui.icons.TelegramIcons
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import com.yalantis.ucrop.UCrop
import ja.burhanrashid52.photoeditor.PhotoEditor
import ja.burhanrashid52.photoeditor.PhotoEditorView
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

private val ViewerExpandEasing = CubicBezierEasing(0.25f, 0.1f, 0.25f, 1.0f)
private val ViewerHorizontalSwipeEasing = CubicBezierEasing(0.22f, 1.0f, 0.36f, 1.0f)
private const val ViewerSwipeSettleDurationMs = 220

private data class SimpleViewerTransform(
    val scaleX: Float,
    val scaleY: Float,
    val translationX: Float,
    val translationY: Float,
    val cornerRadiusDp: Float
)

private enum class SwipeAxis {
    HORIZONTAL,
    VERTICAL
}

private fun lerpFloat(start: Float, stop: Float, fraction: Float): Float {
    return start + (stop - start) * fraction
}

private fun setupFullscreenWindow(window: Window?) {
    window ?: return
    WindowCompat.setDecorFitsSystemWindows(window, false)
    window.setLayout(
        WindowManager.LayoutParams.MATCH_PARENT,
        WindowManager.LayoutParams.MATCH_PARENT
    )
    window.setBackgroundDrawable(ColorDrawable(AndroidColor.TRANSPARENT))
    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
    window.decorView.setPadding(0, 0, 0, 0)

    val attrs = window.attributes
    attrs.width = WindowManager.LayoutParams.MATCH_PARENT
    attrs.height = WindowManager.LayoutParams.MATCH_PARENT
    attrs.layoutInDisplayCutoutMode =
        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    window.attributes = attrs

    val decorView = window.decorView
    val telegramLikeFlags =
        View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    if (decorView.systemUiVisibility != telegramLikeFlags) {
        decorView.systemUiVisibility = telegramLikeFlags
    }

    window.statusBarColor = AndroidColor.TRANSPARENT
    window.navigationBarColor = AndroidColor.TRANSPARENT
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        window.isStatusBarContrastEnforced = false
        window.isNavigationBarContrastEnforced = false
    }
    val controller = WindowCompat.getInsetsController(window, decorView)
    controller.isAppearanceLightStatusBars = false
    controller.isAppearanceLightNavigationBars = false
    controller.show(WindowInsetsCompat.Type.statusBars())
    controller.show(WindowInsetsCompat.Type.navigationBars())
    window.setWindowAnimations(0)
}

@Composable
fun SimpleFullscreenPhotoViewer(
    imageUri: Uri,
    onDismiss: () -> Unit,
    sourceThumbnail: ThumbnailPosition? = null,
    galleryImageUris: List<Uri> = emptyList(),
    initialGalleryIndex: Int = 0,
    showCaptionInput: Boolean = false,
    caption: String = "",
    onCaptionChange: ((String) -> Unit)? = null,
    onSend: ((Uri, String) -> Unit)? = null,
    isDarkTheme: Boolean = true
) {
    Dialog(
        onDismissRequest = onDismiss,
        properties =
            DialogProperties(
                dismissOnBackPress = false,
                dismissOnClickOutside = false,
                usePlatformDefaultWidth = false,
                decorFitsSystemWindows = false
            )
    ) {
        val view = LocalView.current
        SideEffect {
            val dialogWindow = (view.parent as? DialogWindowProvider)?.window
            setupFullscreenWindow(dialogWindow)
        }
        SimpleFullscreenPhotoContent(
            imageUri = imageUri,
            onDismiss = onDismiss,
            sourceThumbnail = sourceThumbnail,
            galleryImageUris = galleryImageUris,
            initialGalleryIndex = initialGalleryIndex,
            showCaptionInput = showCaptionInput,
            caption = caption,
            onCaptionChange = onCaptionChange,
            onSend = onSend,
            isDarkTheme = isDarkTheme
        )
    }
}

@Composable
fun SimpleFullscreenPhotoOverlay(
    imageUri: Uri,
    onDismiss: () -> Unit,
    sourceThumbnail: ThumbnailPosition? = null,
    galleryImageUris: List<Uri> = emptyList(),
    initialGalleryIndex: Int = 0,
    modifier: Modifier = Modifier,
    showCaptionInput: Boolean = false,
    caption: String = "",
    onCaptionChange: ((String) -> Unit)? = null,
    onSend: ((Uri, String) -> Unit)? = null,
    isDarkTheme: Boolean = true
) {
    SimpleFullscreenPhotoContent(
        imageUri = imageUri,
        onDismiss = onDismiss,
        sourceThumbnail = sourceThumbnail,
        galleryImageUris = galleryImageUris,
        initialGalleryIndex = initialGalleryIndex,
        modifier = modifier,
        showCaptionInput = showCaptionInput,
        caption = caption,
        onCaptionChange = onCaptionChange,
        onSend = onSend,
        isDarkTheme = isDarkTheme
    )
}

@Composable
private fun SimpleFullscreenPhotoContent(
    imageUri: Uri,
    onDismiss: () -> Unit,
    sourceThumbnail: ThumbnailPosition? = null,
    galleryImageUris: List<Uri> = emptyList(),
    initialGalleryIndex: Int = 0,
    modifier: Modifier = Modifier,
    showCaptionInput: Boolean = false,
    caption: String = "",
    onCaptionChange: ((String) -> Unit)? = null,
    onSend: ((Uri, String) -> Unit)? = null,
    isDarkTheme: Boolean = true
) {
    val context = LocalContext.current
    val view = LocalView.current
    val focusManager = LocalFocusManager.current
    val density = LocalDensity.current
    val scope = rememberCoroutineScope()

    var isClosing by remember { mutableStateOf(false) }
    var screenSize by remember { mutableStateOf(IntSize.Zero) }
    var showEmojiPicker by remember { mutableStateOf(false) }
    var editTextView by remember { mutableStateOf<AppleEmojiEditTextView?>(null) }
    var lastToggleTime by remember { mutableLongStateOf(0L) }
    var isKeyboardVisible by remember { mutableStateOf(false) }
    var lastStableKeyboardHeight by remember { mutableStateOf(0.dp) }
    var localCaption by remember(imageUri) { mutableStateOf("") }
    val normalizedGalleryImageUris = remember(imageUri, galleryImageUris) {
        val filtered = galleryImageUris.distinct()
        when {
            filtered.isEmpty() -> listOf(imageUri)
            filtered.contains(imageUri) -> filtered
            else -> listOf(imageUri) + filtered
        }
    }
    var currentGalleryIndex by remember(imageUri, normalizedGalleryImageUris, initialGalleryIndex) {
        val fallbackIndex =
            normalizedGalleryImageUris.indexOf(imageUri).takeIf { it >= 0 } ?: 0
        mutableStateOf(initialGalleryIndex.coerceIn(0, normalizedGalleryImageUris.lastIndex.coerceAtLeast(0)).takeIf {
            normalizedGalleryImageUris.getOrNull(it) != null
        } ?: fallbackIndex)
    }
    var currentImageUri by remember(imageUri, normalizedGalleryImageUris) {
        mutableStateOf(
            normalizedGalleryImageUris.getOrNull(currentGalleryIndex)
                ?: normalizedGalleryImageUris.firstOrNull()
                ?: imageUri
        )
    }
    var currentTool by remember { mutableStateOf(EditorTool.NONE) }
    var selectedColor by remember { mutableStateOf(Color.White) }
    var brushSize by remember { mutableStateOf(12f) }
    var showColorPicker by remember { mutableStateOf(false) }
    var isEraserActive by remember { mutableStateOf(false) }
    var isSaving by remember { mutableStateOf(false) }
    var photoEditor by remember { mutableStateOf<PhotoEditor?>(null) }
    var photoEditorView by remember { mutableStateOf<PhotoEditorView?>(null) }
    var hasDrawingEdits by remember { mutableStateOf(false) }
    var rotationAngle by remember { mutableStateOf(0f) }
    var isFlippedHorizontally by remember { mutableStateOf(false) }
    var isFlippedVertically by remember { mutableStateOf(false) }
    var swipeAxis by remember { mutableStateOf<SwipeAxis?>(null) }
    var swipeOffsetX by remember { mutableFloatStateOf(0f) }
    var swipeOffsetY by remember { mutableFloatStateOf(0f) }
    var isSwipeAnimating by remember { mutableStateOf(false) }
    var isSwitchingImage by remember { mutableStateOf(false) }
    var editorLoadingUri by remember { mutableStateOf<Uri?>(null) }
    var editorCoverUri by remember { mutableStateOf<Uri?>(null) }

    val progress = remember(imageUri, sourceThumbnail) {
        Animatable(if (sourceThumbnail != null) 0f else 1f)
    }
    val coordinator = rememberKeyboardTransitionCoordinator()
    val imeInsets = WindowInsets.ime
    val toggleCooldownMs = 500L

    val cropLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            result.data?.let { data ->
                UCrop.getOutput(data)?.let { croppedUri ->
                    currentImageUri = croppedUri
                    rotationAngle = 0f
                    isFlippedHorizontally = false
                    isFlippedVertically = false
                    currentTool = EditorTool.NONE
                    showColorPicker = false
                    isEraserActive = false
                }
            }
        }
    }

    val captionText = if (onCaptionChange != null) caption else localCaption
    val updateCaption: (String) -> Unit = { value ->
        if (onCaptionChange != null) {
            onCaptionChange(value)
        } else {
            localCaption = value
        }
    }
    val isDrawingModeActive = currentTool == EditorTool.DRAW

    LaunchedEffect(imageUri, sourceThumbnail) {
        localCaption = caption
        val clampedIndex = initialGalleryIndex.coerceIn(
            0,
            normalizedGalleryImageUris.lastIndex.coerceAtLeast(0)
        )
        currentGalleryIndex = clampedIndex
        currentImageUri =
            normalizedGalleryImageUris.getOrNull(clampedIndex)
                ?: normalizedGalleryImageUris.firstOrNull()
                ?: imageUri
        editorLoadingUri = null
        editorCoverUri = null
        if (progress.value < 1f) {
            progress.animateTo(
                targetValue = 1f,
                animationSpec = tween(durationMillis = 230, easing = ViewerExpandEasing)
            )
        }
    }

    LaunchedEffect(showCaptionInput) {
        if (!showCaptionInput) return@LaunchedEffect
        snapshotFlow { with(density) { imeInsets.getBottom(density).toDp() } }.collect { currentImeHeight ->
            isKeyboardVisible = currentImeHeight > 50.dp
            coordinator.updateKeyboardHeight(currentImeHeight)
            if (currentImeHeight > 100.dp) {
                coordinator.syncHeights()
                lastStableKeyboardHeight = currentImeHeight
            }
        }
    }

    LaunchedEffect(showCaptionInput) {
        if (showCaptionInput) {
            KeyboardHeightProvider.getSavedKeyboardHeight(context)
        }
    }

    LaunchedEffect(showCaptionInput, isKeyboardVisible, showEmojiPicker, lastStableKeyboardHeight) {
        if (!showCaptionInput) return@LaunchedEffect
        if (isKeyboardVisible && !showEmojiPicker) {
            delay(350)
            if (isKeyboardVisible && !showEmojiPicker && lastStableKeyboardHeight > 300.dp) {
                val heightPx = with(density) { lastStableKeyboardHeight.toPx().toInt() }
                KeyboardHeightProvider.saveKeyboardHeight(context, heightPx)
            }
        }
    }

    LaunchedEffect(isDrawingModeActive) {
        if (isDrawingModeActive) {
            showEmojiPicker = false
            val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(view.windowToken, 0)
            focusManager.clearFocus(force = true)
        }
    }

    fun hideKeyboard() {
        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(view.windowToken, 0)
    }

    fun closeViewer() {
        if (isClosing) return
        isClosing = true
        showEmojiPicker = false
        hideKeyboard()
        focusManager.clearFocus(force = true)
        scope.launch {
            progress.animateTo(
                targetValue = 0f,
                animationSpec = tween(durationMillis = 210, easing = ViewerExpandEasing)
            )
            onDismiss()
        }
    }

    fun resetEditorStateForImageSwitch() {
        currentTool = EditorTool.NONE
        showColorPicker = false
        isEraserActive = false
        hasDrawingEdits = false
        rotationAngle = 0f
        isFlippedHorizontally = false
        isFlippedVertically = false
        photoEditor?.setBrushDrawingMode(false)
        photoEditor?.clearAllViews()
    }

    fun toggleEmojiPicker() {
        val now = System.currentTimeMillis()
        if (now - lastToggleTime < toggleCooldownMs) {
            return
        }
        lastToggleTime = now

        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        if (coordinator.isEmojiVisible) {
            coordinator.requestShowKeyboard(
                showKeyboard = {
                    editTextView?.let { editText ->
                        editText.requestFocus()
                        imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
                    }
                },
                hideEmoji = { showEmojiPicker = false }
            )
        } else {
            coordinator.requestShowEmoji(
                hideKeyboard = {
                    imm.hideSoftInputFromWindow(view.windowToken, 0)
                },
                showEmoji = { showEmojiPicker = true }
            )
        }
    }

    BackHandler { closeViewer() }

    val canUseSwipeGestures =
        currentTool == EditorTool.NONE &&
            !isClosing &&
            !isSaving &&
            !showColorPicker &&
            !isKeyboardVisible &&
            !showEmojiPicker &&
            !coordinator.isEmojiBoxVisible
    val canSwipeHorizontally = canUseSwipeGestures && normalizedGalleryImageUris.size > 1

    val animatedSwipeOffsetX by animateFloatAsState(
        targetValue = swipeOffsetX,
        animationSpec =
            if (isSwipeAnimating) {
                tween(ViewerSwipeSettleDurationMs, easing = ViewerHorizontalSwipeEasing)
            } else {
                snap()
            },
        label = "simple_viewer_swipe_x"
    )
    val animatedSwipeOffsetY by animateFloatAsState(
        targetValue = swipeOffsetY,
        animationSpec = if (isSwipeAnimating) tween(180, easing = ViewerExpandEasing) else snap(),
        label = "simple_viewer_swipe_y"
    )

    val isVerticalSwipeActive = swipeAxis == SwipeAxis.VERTICAL || kotlin.math.abs(animatedSwipeOffsetY) > 0.5f
    val swipeDismissProgress =
        if (isVerticalSwipeActive && screenSize.height > 0) {
            (kotlin.math.abs(animatedSwipeOffsetY) / screenSize.height.toFloat()).coerceIn(0f, 1f)
        } else {
            0f
        }
    val swipeScrimAlpha = 1f - swipeDismissProgress * 0.45f

    val swipeGestureModifier =
        if (canUseSwipeGestures) {
            Modifier.pointerInput(
                currentGalleryIndex,
                normalizedGalleryImageUris,
                canSwipeHorizontally,
                screenSize,
                isSwitchingImage
            ) {
                detectDragGestures(
                    onDragStart = {
                        if (isSwitchingImage) return@detectDragGestures
                        swipeAxis = null
                        isSwipeAnimating = false
                    },
                    onDragCancel = {
                        swipeAxis = null
                        isSwipeAnimating = true
                        swipeOffsetX = 0f
                        swipeOffsetY = 0f
                    },
                    onDragEnd = {
                        val width = screenSize.width.toFloat().coerceAtLeast(1f)
                        val height = screenSize.height.toFloat().coerceAtLeast(1f)
                        val horizontalThreshold = width * 0.2f
                        val verticalThreshold = height * 0.12f

                        when (swipeAxis) {
                            SwipeAxis.HORIZONTAL -> {
                                val targetIndex =
                                    when {
                                        swipeOffsetX <= -horizontalThreshold &&
                                            currentGalleryIndex < normalizedGalleryImageUris.lastIndex -> {
                                            currentGalleryIndex + 1
                                        }
                                        swipeOffsetX >= horizontalThreshold && currentGalleryIndex > 0 -> {
                                            currentGalleryIndex - 1
                                        }
                                        else -> -1
                                    }

                                if (targetIndex >= 0) {
                                    val targetUri = normalizedGalleryImageUris[targetIndex]
                                    context.imageLoader.enqueue(
                                        ImageRequest.Builder(context)
                                            .data(targetUri)
                                            .crossfade(false)
                                            .build()
                                    )
                                    val toNext = targetIndex > currentGalleryIndex
                                    isSwitchingImage = true
                                    isSwipeAnimating = true
                                    val exitOffset = if (toNext) -width else width
                                    swipeOffsetX = exitOffset
                                    scope.launch {
                                        delay(ViewerSwipeSettleDurationMs.toLong())
                                        currentGalleryIndex = targetIndex
                                        currentImageUri = targetUri
                                        editorCoverUri = targetUri
                                        resetEditorStateForImageSwitch()
                                        isSwipeAnimating = false
                                        swipeOffsetX = 0f
                                        delay(90)
                                        isSwitchingImage = false
                                    }
                                } else {
                                    isSwipeAnimating = true
                                    swipeOffsetX = 0f
                                }
                            }
                            SwipeAxis.VERTICAL -> {
                                if (kotlin.math.abs(swipeOffsetY) >= verticalThreshold) {
                                    closeViewer()
                                } else {
                                    isSwipeAnimating = true
                                    swipeOffsetY = 0f
                                }
                            }
                            null -> {
                                isSwipeAnimating = true
                                swipeOffsetX = 0f
                                swipeOffsetY = 0f
                            }
                        }
                        swipeAxis = null
                    },
                    onDrag = { change, dragAmount ->
                        if (isSwitchingImage) return@detectDragGestures
                        if (swipeAxis == null) {
                            val absX = kotlin.math.abs(dragAmount.x)
                            val absY = kotlin.math.abs(dragAmount.y)
                            swipeAxis =
                                if (absX > absY && canSwipeHorizontally) {
                                    SwipeAxis.HORIZONTAL
                                } else {
                                    SwipeAxis.VERTICAL
                                }
                        }

                        when (swipeAxis) {
                            SwipeAxis.HORIZONTAL -> {
                                val width = screenSize.width.toFloat().coerceAtLeast(1f)
                                val atFirst = currentGalleryIndex <= 0
                                val atLast = currentGalleryIndex >= normalizedGalleryImageUris.lastIndex
                                var next = swipeOffsetX + dragAmount.x
                                if ((next > 0f && atFirst) || (next < 0f && atLast)) {
                                    next *= 0.32f
                                }
                                swipeOffsetX = next.coerceIn(-width, width)
                                swipeOffsetY = 0f
                                change.consume()
                            }
                            SwipeAxis.VERTICAL -> {
                                val height = screenSize.height.toFloat().coerceAtLeast(1f)
                                swipeOffsetY = (swipeOffsetY + dragAmount.y).coerceIn(-height, height)
                                swipeOffsetX = 0f
                                change.consume()
                            }
                            null -> Unit
                        }
                    }
                )
            }
        } else {
            Modifier
        }

    val transform by remember(sourceThumbnail, screenSize, progress.value) {
        derivedStateOf {
            val p = progress.value
            if (sourceThumbnail != null && screenSize != IntSize.Zero) {
                val screenW = screenSize.width.toFloat().coerceAtLeast(1f)
                val screenH = screenSize.height.toFloat().coerceAtLeast(1f)
                val srcW = sourceThumbnail.width.coerceAtLeast(1f)
                val srcH = sourceThumbnail.height.coerceAtLeast(1f)

                val sourceScaleX = srcW / screenW
                val sourceScaleY = srcH / screenH

                val targetCenterX = screenW / 2f
                val targetCenterY = screenH / 2f
                val sourceCenterX = sourceThumbnail.x + srcW / 2f
                val sourceCenterY = sourceThumbnail.y + srcH / 2f

                SimpleViewerTransform(
                    scaleX = lerpFloat(sourceScaleX, 1f, p),
                    scaleY = lerpFloat(sourceScaleY, 1f, p),
                    translationX = lerpFloat(sourceCenterX - targetCenterX, 0f, p),
                    translationY = lerpFloat(sourceCenterY - targetCenterY, 0f, p),
                    cornerRadiusDp = lerpFloat(sourceThumbnail.cornerRadius, 0f, p)
                )
            } else {
                SimpleViewerTransform(
                    scaleX = lerpFloat(0.94f, 1f, p),
                    scaleY = lerpFloat(0.94f, 1f, p),
                    translationX = 0f,
                    translationY = 0f,
                    cornerRadiusDp = 0f
                )
            }
        }
    }

    val tapToDismissModifier =
        if (!showCaptionInput) {
            Modifier.pointerInput(currentImageUri) { detectTapGestures(onTap = { closeViewer() }) }
        } else {
            Modifier
        }

    val horizontalNeighborIndex =
        remember(currentGalleryIndex, normalizedGalleryImageUris, animatedSwipeOffsetX) {
            when {
                animatedSwipeOffsetX < 0f && currentGalleryIndex < normalizedGalleryImageUris.lastIndex ->
                    currentGalleryIndex + 1
                animatedSwipeOffsetX > 0f && currentGalleryIndex > 0 ->
                    currentGalleryIndex - 1
                else -> -1
            }
        }
    val horizontalNeighborUri =
        normalizedGalleryImageUris.getOrNull(horizontalNeighborIndex)
    val shouldShowHorizontalNeighbor =
        horizontalNeighborUri != null && kotlin.math.abs(animatedSwipeOffsetX) > 0.5f
    val shouldShowHorizontalCurrentLayer =
        canSwipeHorizontally &&
            (swipeAxis == SwipeAxis.HORIZONTAL ||
                isSwitchingImage ||
                kotlin.math.abs(animatedSwipeOffsetX) > 0.5f ||
                editorCoverUri != null)
    val horizontalNeighborTranslationX =
        if (animatedSwipeOffsetX < 0f) {
            animatedSwipeOffsetX + screenSize.width.toFloat()
        } else {
            animatedSwipeOffsetX - screenSize.width.toFloat()
        }

    Box(
        modifier =
            modifier.fillMaxSize()
                .onSizeChanged { screenSize = it }
                .background(Color.Black.copy(alpha = swipeScrimAlpha))
                .then(tapToDismissModifier),
        contentAlignment = Alignment.Center
    ) {
        if (shouldShowHorizontalNeighbor) {
            AsyncImage(
                model =
                    ImageRequest.Builder(context)
                        .data(horizontalNeighborUri)
                        .crossfade(false)
                        .build(),
                contentDescription = null,
                modifier =
                    Modifier.fillMaxSize()
                        .graphicsLayer {
                            scaleX = transform.scaleX
                            scaleY = transform.scaleY
                            translationX = transform.translationX + horizontalNeighborTranslationX
                            translationY = transform.translationY + animatedSwipeOffsetY
                        }
                        .then(
                            if (transform.cornerRadiusDp > 0f) {
                                Modifier.clip(RoundedCornerShape(transform.cornerRadiusDp.dp))
                            } else {
                                Modifier
                            }
                        ),
                contentScale = ContentScale.Crop
            )
        }

        if (shouldShowHorizontalCurrentLayer) {
            AsyncImage(
                model =
                    ImageRequest.Builder(context)
                        .data(currentImageUri)
                        .crossfade(false)
                        .build(),
                contentDescription = null,
                modifier =
                    Modifier.fillMaxSize()
                        .graphicsLayer {
                            scaleX = transform.scaleX
                            scaleY = transform.scaleY
                            translationX = transform.translationX + animatedSwipeOffsetX
                            translationY = transform.translationY + animatedSwipeOffsetY
                        }
                        .then(
                            if (transform.cornerRadiusDp > 0f) {
                                Modifier.clip(RoundedCornerShape(transform.cornerRadiusDp.dp))
                            } else {
                                Modifier
                            }
                        ),
                contentScale = ContentScale.Crop
            )
        }

        AndroidView(
            factory = { ctx ->
                PhotoEditorView(ctx).apply {
                    photoEditorView = this
                    setPadding(0, 0, 0, 0)
                    setBackgroundColor(android.graphics.Color.BLACK)
                    source.apply {
                        scaleType = ImageView.ScaleType.CENTER_CROP
                        adjustViewBounds = false
                        setPadding(0, 0, 0, 0)
                        setImageURI(currentImageUri)
                        tag = currentImageUri
                    }
                    photoEditor = PhotoEditor.Builder(ctx, this)
                        .setPinchTextScalable(true)
                        .setClipSourceImage(true)
                        .build()
                }
            },
            update = { editorView ->
                if (editorView.source.tag != currentImageUri) {
                    if (editorLoadingUri != currentImageUri) {
                        val requestUri = currentImageUri
                        editorLoadingUri = requestUri
                        context.imageLoader.enqueue(
                            ImageRequest.Builder(context)
                                .data(requestUri)
                                .crossfade(false)
                                .target(
                                    onSuccess = { result ->
                                        if (editorLoadingUri == requestUri) {
                                            editorView.source.setImageDrawable(result)
                                            editorView.source.tag = requestUri
                                            editorLoadingUri = null
                                            if (editorCoverUri == requestUri) {
                                                editorCoverUri = null
                                            }
                                        }
                                    },
                                    onError = { error ->
                                        if (editorLoadingUri == requestUri) {
                                            if (error != null) {
                                                editorView.source.setImageDrawable(error)
                                            } else {
                                                editorView.source.setImageURI(requestUri)
                                            }
                                            editorView.source.tag = requestUri
                                            editorLoadingUri = null
                                            if (editorCoverUri == requestUri) {
                                                editorCoverUri = null
                                            }
                                        }
                                    }
                                )
                                .build()
                        )
                    }
                }
                editorView.source.rotation = rotationAngle
                editorView.source.scaleX = if (isFlippedHorizontally) -1f else 1f
                editorView.source.scaleY = if (isFlippedVertically) -1f else 1f
                if (editorView.source.tag == currentImageUri && editorCoverUri == currentImageUri) {
                    editorCoverUri = null
                }
            },
            modifier =
                Modifier.fillMaxSize()
                    .then(swipeGestureModifier)
                    .graphicsLayer {
                        alpha = if (shouldShowHorizontalCurrentLayer) 0f else 1f
                        scaleX = transform.scaleX
                        scaleY = transform.scaleY
                        translationX = transform.translationX + animatedSwipeOffsetX
                        translationY = transform.translationY + animatedSwipeOffsetY
                    }
                    .then(
                        if (transform.cornerRadiusDp > 0f) {
                            Modifier.clip(RoundedCornerShape(transform.cornerRadiusDp.dp))
                        } else {
                            Modifier
                        }
                    )
        )

        if (showCaptionInput) {
            Box(
                modifier =
                    Modifier.fillMaxWidth()
                        .align(Alignment.TopCenter)
                        .background(
                            Brush.verticalGradient(
                                colors =
                                    listOf(
                                        Color.Black.copy(alpha = 0.55f),
                                        Color.Transparent
                                    )
                            )
                        )
                        .statusBarsPadding()
                        .padding(horizontal = 4.dp, vertical = 8.dp)
            ) {
                if (!isDrawingModeActive) {
                    IconButton(
                        onClick = { closeViewer() },
                        modifier = Modifier.align(Alignment.CenterStart)
                    ) {
                        Icon(
                            painter = TelegramIcons.Close,
                            contentDescription = "Close",
                            tint = Color.White,
                            modifier = Modifier.size(28.dp)
                        )
                    }
                }
            }

            AnimatedVisibility(
                visible = currentTool == EditorTool.DRAW && showColorPicker,
                enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
                exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
                modifier =
                    Modifier.align(Alignment.BottomCenter)
                        .padding(bottom = 132.dp)
            ) {
                TelegramColorPicker(
                    selectedColor = selectedColor,
                    brushSize = brushSize,
                    onColorSelected = { color ->
                        selectedColor = color
                        photoEditor?.brushColor = color.toArgb()
                    },
                    onBrushSizeChanged = { size ->
                        brushSize = size
                        photoEditor?.brushSize = size
                    }
                )
            }

            AnimatedVisibility(
                visible = currentTool == EditorTool.ROTATE,
                enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
                exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
                modifier =
                    Modifier.align(Alignment.BottomCenter)
                        .padding(bottom = 132.dp)
            ) {
                TelegramRotateBar(
                    onRotateLeft = { rotationAngle = (rotationAngle - 90f) % 360f },
                    onRotateRight = { rotationAngle = (rotationAngle + 90f) % 360f },
                    onFlipHorizontal = { isFlippedHorizontally = !isFlippedHorizontally },
                    onFlipVertical = { isFlippedVertically = !isFlippedVertically }
                )
            }

            val shouldUseImePadding = !coordinator.isEmojiBoxVisible
            val shouldAddNavBarPadding = !isKeyboardVisible && !coordinator.isEmojiBoxVisible

            Column(
                modifier =
                    Modifier.fillMaxWidth()
                        .align(Alignment.BottomCenter)
                        .then(if (shouldUseImePadding) Modifier.imePadding() else Modifier)
            ) {
                AnimatedVisibility(
                    visible = !isKeyboardVisible && !showEmojiPicker && !coordinator.isEmojiBoxVisible,
                    enter = fadeIn() + slideInVertically { it },
                    exit = fadeOut() + slideOutVertically { it }
                ) {
                    Box(
                        modifier =
                            Modifier.fillMaxWidth()
                                .background(
                                    Brush.verticalGradient(
                                        colors =
                                            listOf(
                                                Color.Transparent,
                                                Color.Black.copy(alpha = 0.6f)
                                            )
                                    )
                                )
                    ) {
                        TelegramToolbar(
                            currentTool = currentTool,
                            showCaptionInput = true,
                            isSaving = isSaving,
                            isEraserActive = isEraserActive,
                            onCropClick = {
                                currentTool = EditorTool.NONE
                                showColorPicker = false
                                isEraserActive = false
                                photoEditor?.setBrushDrawingMode(false)
                                launchCrop(context, currentImageUri, cropLauncher)
                            },
                            onRotateClick = {
                                currentTool =
                                    if (currentTool == EditorTool.ROTATE) EditorTool.NONE
                                    else EditorTool.ROTATE
                                showColorPicker = false
                                isEraserActive = false
                                photoEditor?.setBrushDrawingMode(false)
                            },
                            onDrawClick = {
                                if (currentTool == EditorTool.DRAW) {
                                    if (isEraserActive) {
                                        isEraserActive = false
                                        photoEditor?.setBrushDrawingMode(true)
                                        photoEditor?.brushColor = selectedColor.toArgb()
                                        photoEditor?.brushSize = brushSize
                                    } else {
                                        showColorPicker = !showColorPicker
                                    }
                                } else {
                                    currentTool = EditorTool.DRAW
                                    hasDrawingEdits = true
                                    isEraserActive = false
                                    photoEditor?.setBrushDrawingMode(true)
                                    photoEditor?.brushColor = selectedColor.toArgb()
                                    photoEditor?.brushSize = brushSize
                                    showColorPicker = true
                                }
                            },
                            onEraserClick = {
                                isEraserActive = !isEraserActive
                                if (isEraserActive) {
                                    photoEditor?.brushEraser()
                                } else {
                                    photoEditor?.setBrushDrawingMode(true)
                                    photoEditor?.brushColor = selectedColor.toArgb()
                                    photoEditor?.brushSize = brushSize
                                }
                            },
                            onDrawDoneClick = {
                                currentTool = EditorTool.NONE
                                showColorPicker = false
                                isEraserActive = false
                                photoEditor?.setBrushDrawingMode(false)
                            },
                            onDoneClick = {}
                        )
                    }
                }

                AnimatedVisibility(
                    visible = !isDrawingModeActive,
                    enter = fadeIn() + slideInVertically { it / 2 },
                    exit = fadeOut() + slideOutVertically { it / 2 }
                ) {
                    Box(
                        modifier =
                            Modifier.fillMaxWidth()
                                .background(Color.Black.copy(alpha = 0.75f))
                                .padding(
                                    start = 12.dp,
                                    end = 12.dp,
                                    top = 10.dp,
                                    bottom =
                                        if (isKeyboardVisible || coordinator.isEmojiBoxVisible) 10.dp
                                        else 16.dp
                                )
                                .then(
                                    if (shouldAddNavBarPadding) Modifier.navigationBarsPadding()
                                    else Modifier
                                )
                    ) {
                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            verticalAlignment = Alignment.CenterVertically,
                            horizontalArrangement = Arrangement.spacedBy(10.dp)
                        ) {
                            IconButton(
                                onClick = { toggleEmojiPicker() },
                                modifier = Modifier.size(32.dp)
                            ) {
                                Crossfade(
                                    targetState = showEmojiPicker,
                                    animationSpec = tween(150),
                                    label = "simpleViewerEmojiToggle"
                                ) { isEmoji ->
                                    Icon(
                                        painter = if (isEmoji) TelegramIcons.Keyboard else TelegramIcons.Smile,
                                        contentDescription = if (isEmoji) "Keyboard" else "Emoji",
                                        tint = Color.White.copy(alpha = 0.72f),
                                        modifier = Modifier.size(26.dp)
                                    )
                                }
                            }

                            Box(
                                modifier =
                                    Modifier.weight(1f)
                                        .heightIn(min = 24.dp, max = 100.dp)
                            ) {
                                AppleEmojiTextField(
                                    value = captionText,
                                    onValueChange = updateCaption,
                                    textColor = Color.White,
                                    textSize = 16f,
                                    hint = "Add a caption...",
                                    hintColor = Color.White.copy(alpha = 0.5f),
                                    modifier = Modifier.fillMaxWidth(),
                                    requestFocus = false,
                                    onViewCreated = { textView -> editTextView = textView },
                                    onFocusChanged = { hasFocus ->
                                        if (hasFocus && showEmojiPicker) {
                                            toggleEmojiPicker()
                                        }
                                    }
                                )
                            }

                            Box(
                                modifier =
                                    Modifier.size(44.dp)
                                        .shadow(
                                            elevation = 4.dp,
                                            shape = CircleShape,
                                            clip = false
                                        )
                                        .clip(CircleShape)
                                        .background(PrimaryBlue)
                                        .clickable(enabled = !isSaving) {
                                            if (isSaving || isClosing) return@clickable
                                            showEmojiPicker = false
                                            hideKeyboard()
                                            focusManager.clearFocus(force = true)
                                            scope.launch {
                                                isSaving = true
                                                val savedUri =
                                                    saveEditedImageSync(
                                                        context = context,
                                                        photoEditor = photoEditor,
                                                        photoEditorView = photoEditorView,
                                                        imageUri = currentImageUri,
                                                        hasDrawingEdits = hasDrawingEdits
                                                    )
                                                isSaving = false
                                                val finalUri = savedUri ?: currentImageUri
                                                if (onSend != null) {
                                                    onSend(finalUri, captionText)
                                                } else {
                                                    closeViewer()
                                                }
                                            }
                                        },
                                contentAlignment = Alignment.Center
                            ) {
                                if (isSaving) {
                                    CircularProgressIndicator(
                                        modifier = Modifier.size(18.dp),
                                        color = Color.White,
                                        strokeWidth = 2.dp
                                    )
                                } else {
                                    Icon(
                                        painter = TelegramIcons.Send,
                                        contentDescription = "Send",
                                        tint = Color.White,
                                        modifier =
                                            Modifier.size(24.dp)
                                                .offset(x = 1.dp)
                                    )
                                }
                            }
                        }
                    }
                }

                if (!isDrawingModeActive) {
                    AnimatedKeyboardTransition(
                        coordinator = coordinator,
                        showEmojiPicker = showEmojiPicker
                    ) {
                        OptimizedEmojiPicker(
                            isVisible = true,
                            isDarkTheme = isDarkTheme,
                            onEmojiSelected = { emoji -> updateCaption(captionText + emoji) },
                            onClose = { toggleEmojiPicker() },
                            modifier = Modifier.fillMaxWidth()
                        )
                    }
                }
            }
        }
    }
}
