/*
 * Episteme Reader - A native Android document reader.
 * Copyright (C) 2026 Episteme
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * mail: epistemereader@gmail.com
 */
// PaginatedReader.kt
package com.aryan.reader.paginatedreader

import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
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.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.Clipboard
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalTextToolbar
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.TextToolbar
import androidx.compose.ui.platform.TextToolbarStatus
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextMeasurer
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.zIndex
import androidx.core.net.toUri
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.imageLoader
import coil.request.ImageRequest.Builder
import com.aryan.reader.R
import com.aryan.reader.countWords
import com.aryan.reader.epub.EpubBook
import com.aryan.reader.epubreader.HighlightColor
import com.aryan.reader.epubreader.PaletteManagerDialog
import com.aryan.reader.epubreader.ReaderTextAlign
import com.aryan.reader.epubreader.SpectrumButton
import com.aryan.reader.epubreader.TtsHighlightInfo
import com.aryan.reader.epubreader.UserHighlight
import com.aryan.reader.paginatedreader.data.BookCacheDatabase
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.protobuf.ProtoBuf
import timber.log.Timber
import java.io.File
import kotlin.math.abs
import kotlin.math.roundToInt

data class PaginatedSelection(
    val blockIndex: Int,
    val baseCfi: String,
    val startOffset: Int,
    val endOffset: Int,
    val text: String,
    val rect: Rect
)

private class SmartPopupPositionProvider(
    private val contentRect: Rect, private val density: Density
) : PopupPositionProvider {
    override fun calculatePosition(
        anchorBounds: IntRect,
        windowSize: IntSize,
        layoutDirection: LayoutDirection,
        popupContentSize: IntSize
    ): IntOffset {
        val padding = with(density) { 8.dp.roundToPx() }
        val popupWidth = popupContentSize.width
        val popupHeight = popupContentSize.height

        var x = (contentRect.center.x - popupWidth / 2).toInt()
        x = x.coerceIn(0, windowSize.width - popupWidth)

        val topY = (contentRect.top - popupHeight - padding).toInt()
        val bottomY = (contentRect.bottom + padding).toInt()

        var y = topY
        if (y < 0) {
            y = if (bottomY + popupHeight <= windowSize.height) {
                bottomY
            } else {
                val spaceTop = contentRect.top
                val spaceBottom = windowSize.height - contentRect.bottom
                if (spaceBottom > spaceTop) {
                    bottomY.coerceAtMost(windowSize.height - popupHeight)
                } else {
                    topY.coerceAtLeast(0)
                }
            }
        }

        return IntOffset(x, y)
    }
}

internal object CfiUtils {
    fun compare(cfi1: String, cfi2: String): Int {
        val path1 = cfi1.split(':').first()
        val path2 = cfi2.split(':').first()

        val parts1 = path1.split('/').filter { it.isNotEmpty() }.mapNotNull { it.toIntOrNull() }
        val parts2 = path2.split('/').filter { it.isNotEmpty() }.mapNotNull { it.toIntOrNull() }

        val length = minOf(parts1.size, parts2.size)
        for (i in 0 until length) {
            val cmp = parts1[i].compareTo(parts2[i])
            if (cmp != 0) return cmp
        }

        if (parts1.size != parts2.size) {
            return parts1.size.compareTo(parts2.size)
        }

        val offset1 = cfi1.substringAfter(':', "0").toIntOrNull() ?: 0
        val offset2 = cfi2.substringAfter(':', "0").toIntOrNull() ?: 0
        return offset1.compareTo(offset2)
    }

    fun getPath(cfi: String): String = cfi.split(':').first()
    fun getOffset(cfi: String): Int = cfi.substringAfter(':', "0").toIntOrNull() ?: 0
}

private fun highlightQueryInText(
    text: AnnotatedString, query: String, highlightColor: Color
): AnnotatedString {
    if (query.length < 3) return text

    return buildAnnotatedString {
        append(text)
        val textString = text.text
        var startIndex = 0
        while (startIndex < textString.length) {
            val index = textString.indexOf(query, startIndex, ignoreCase = true)
            if (index == -1) break
            addStyle(
                style = SpanStyle(background = highlightColor),
                start = index,
                end = index + query.length
            )
            startIndex = index + query.length
        }
    }
}

@Composable
private fun WrappingContentLayout(
    block: WrappingContentBlock,
    textStyle: TextStyle,
    modifier: Modifier = Modifier,
    searchQuery: String,
    ttsHighlightInfo: TtsHighlightInfo?,
    searchHighlightColor: Color,
    ttsHighlightColor: Color
) {
    val textMeasurer = rememberTextMeasurer()
    val fullText = remember(block.paragraphsToWrap, searchQuery, ttsHighlightInfo) {
        buildAnnotatedString {
            block.paragraphsToWrap.forEachIndexed { index, p ->
                val searchHighlighted =
                    highlightQueryInText(p.content, searchQuery, searchHighlightColor)
                val finalContent = if (ttsHighlightInfo != null && p.cfi == ttsHighlightInfo.cfi) {
                    buildAnnotatedString {
                        append(searchHighlighted)
                        val blockStartAbs = p.startCharOffsetInSource
                        val blockEndAbs = p.startCharOffsetInSource + searchHighlighted.length
                        val highlightStartAbs = ttsHighlightInfo.offset
                        val highlightEndAbs = ttsHighlightInfo.offset + ttsHighlightInfo.text.length
                        val intersectionStartAbs = maxOf(blockStartAbs, highlightStartAbs)
                        val intersectionEndAbs = minOf(blockEndAbs, highlightEndAbs)

                        if (intersectionStartAbs < intersectionEndAbs) {
                            val highlightStartRelative = intersectionStartAbs - blockStartAbs
                            val highlightEndRelative = intersectionEndAbs - blockStartAbs
                            addStyle(
                                style = SpanStyle(
                                    background = ttsHighlightColor
                                ), start = highlightStartRelative, end = highlightEndRelative
                            )
                        }
                    }
                } else {
                    searchHighlighted
                }
                append(finalContent)
                if (index < block.paragraphsToWrap.lastIndex) append("\n\n")
            }
        }
    }
    val (paragraphStartOffsets, paragraphEndOffsetMap) = remember(block.paragraphsToWrap) {
        val starts = mutableSetOf<Int>()
        val endMap = mutableMapOf<Int, Int>()
        var currentOffset = 0
        block.paragraphsToWrap.forEachIndexed { index, p ->
            starts.add(currentOffset)
            currentOffset += p.content.length
            endMap[currentOffset - 1] = index
            if (index < block.paragraphsToWrap.lastIndex) {
                currentOffset += 2
            }
        }
        starts to endMap
    }
    val density = LocalDensity.current
    var textLayouts by remember {
        mutableStateOf<List<Pair<TextLayoutResult, Offset>>>(emptyList())
    }
    var totalHeight by remember { mutableIntStateOf(0) }

    Layout(content = {
        AsyncImage(
            model = Builder(LocalContext.current).data(File(block.floatedImage.path)).build(),
            contentDescription = block.floatedImage.altText,
            contentScale = ContentScale.Fit
        )
    }, modifier = modifier.drawBehind {
        textLayouts.forEach { (layout, offset) ->
            drawText(layout, topLeft = offset)
        }
    }) { measurables, constraints ->
        val (imageRenderWidthPx, imageRenderHeightPx) = run {
            val imageStyle = block.floatedImage.style
            val intrinsicWidth = block.floatedImage.intrinsicWidth
            val intrinsicHeight = block.floatedImage.intrinsicHeight

            if (intrinsicWidth == null || intrinsicHeight == null || intrinsicWidth <= 0f) {
                0f to 0f
            } else {
                val aspectRatio = intrinsicHeight / intrinsicWidth
                val renderWidth = with(density) {
                    var w = intrinsicWidth

                    if (imageStyle.width != Dp.Unspecified) {
                        w = imageStyle.width.toPx()
                    }

                    if (imageStyle.maxWidth != Dp.Unspecified) {
                        w = w.coerceAtMost(imageStyle.maxWidth.toPx())
                    }

                    w.coerceAtMost(constraints.maxWidth.toFloat())
                }
                renderWidth to (renderWidth * aspectRatio)
            }
        }

        val imagePlacable = if (imageRenderWidthPx > 0 && imageRenderHeightPx > 0) {
            measurables.first().measure(
                Constraints.fixed(
                    imageRenderWidthPx.roundToInt(), imageRenderHeightPx.roundToInt()
                )
            )
        } else {
            null
        }

        val effectiveImageWidth = imagePlacable?.width ?: 0
        val effectiveImageHeight = imagePlacable?.height ?: 0

        var currentY = 0f
        var textOffset = 0
        val layouts = mutableListOf<Pair<TextLayoutResult, Offset>>()

        while (textOffset < fullText.length) {
            val isBesideImage = currentY < effectiveImageHeight
            val floatLeft = block.floatedImage.style.float == "left"

            val currentMaxWidth = if (isBesideImage) {
                (constraints.maxWidth - effectiveImageWidth).coerceAtLeast(0)
            } else {
                constraints.maxWidth
            }

            if (currentMaxWidth <= 0) break

            val lineConstraints = constraints.copy(minWidth = 0, maxWidth = currentMaxWidth)
            val remainingText = fullText.subSequence(textOffset, fullText.length)

            val styleForMeasure =
                remainingText.spanStyles.firstOrNull { it.item.fontFamily != null }?.item?.fontFamily?.let {
                    textStyle.copy(fontFamily = it)
                } ?: textStyle

            val layoutResult = textMeasurer.measure(
                remainingText, style = styleForMeasure, constraints = lineConstraints
            )

            val firstLineEndOffset = layoutResult.getLineEnd(0, visibleEnd = true)
            if (firstLineEndOffset == 0 && remainingText.isNotEmpty()) {
                textOffset++
                continue
            }
            if (firstLineEndOffset == 0) break
            val lineText = remainingText.subSequence(0, firstLineEndOffset)
            val isStartOfParagraph = paragraphStartOffsets.contains(textOffset)
            val finalLineText = if (isStartOfParagraph) {
                lineText
            } else {
                val stylesWithIndent =
                    lineText.paragraphStyles.filter { it.item.textIndent != null }
                if (stylesWithIndent.isNotEmpty()) {
                    buildAnnotatedString {
                        append(lineText)
                        stylesWithIndent.forEach {
                            addStyle(
                                it.item.copy(textIndent = TextIndent(0.sp, 0.sp)), it.start, it.end
                            )
                        }
                    }
                } else {
                    lineText
                }
            }

            val lineLayout = textMeasurer.measure(
                finalLineText, style = styleForMeasure, constraints = lineConstraints
            )
            val xOffset = if (isBesideImage && floatLeft) effectiveImageWidth.toFloat() else 0f

            layouts.add(lineLayout to Offset(xOffset, currentY))

            currentY += lineLayout.size.height
            val endOfLineVisibleCharIndex = textOffset + firstLineEndOffset - 1
            val paraIndex = paragraphEndOffsetMap[endOfLineVisibleCharIndex]

            if (paraIndex != null && paraIndex < block.paragraphsToWrap.lastIndex) {
                val currentPara = block.paragraphsToWrap[paraIndex]
                val nextPara = block.paragraphsToWrap[paraIndex + 1]

                val gap = with(density) {
                    val marginBottom = currentPara.style.margin.bottom.toPx()
                    val marginTop = nextPara.style.margin.top.toPx()
                    maxOf(marginBottom, marginTop)
                }
                currentY += gap
            }
            textOffset += firstLineEndOffset
            while (textOffset < fullText.length && fullText[textOffset].isWhitespace()) {
                textOffset++
            }
        }
        textLayouts = layouts
        totalHeight = maxOf(currentY, effectiveImageHeight.toFloat()).roundToInt()
        layout(constraints.maxWidth, totalHeight) {
            if (imagePlacable != null) {
                val imageX = if (block.floatedImage.style.float == "left") 0
                else constraints.maxWidth - effectiveImageWidth
                imagePlacable.placeRelative(x = imageX, y = 0)
            }
        }
    }
}

@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@OptIn(ExperimentalFoundationApi::class, ExperimentalSerializationApi::class, FlowPreview::class)
@Composable
fun PaginatedReaderScreen(
    book: EpubBook,
    isDarkTheme: Boolean,
    effectiveBg: Color,
    effectiveText: Color,
    pagerState: PagerState,
    isPageTurnAnimationEnabled: Boolean,
    searchQuery: String,
    fontSizeMultiplier: Float,
    lineHeightMultiplier: Float,
    fontFamily: FontFamily,
    textAlign: ReaderTextAlign,
    ttsHighlightInfo: TtsHighlightInfo?,
    initialChapterIndexInBook: Int?,
    modifier: Modifier = Modifier,
    onPaginatorReady: (IPaginator) -> Unit,
    onTap: (Offset?) -> Unit,
    isProUser: Boolean,
    isOss: Boolean = false,
    onShowDictionaryUpsellDialog: () -> Unit,
    onWordSelectedForAiDefinition: (String) -> Unit,
    onTranslate: (String) -> Unit,
    onSearch: (String) -> Unit,
    onStartTtsFromSelection: (String, Int) -> Unit,
    userHighlights: List<UserHighlight>,
    onHighlightCreated: (String, String, String) -> Unit,
    onHighlightDeleted: (String) -> Unit,
    activeHighlightPalette: List<HighlightColor>,
    onUpdatePalette: (Int, HighlightColor) -> Unit,
    activeTextureId: String? = null
) {
    LaunchedEffect(userHighlights) {
        Timber.d("PaginatedReaderScreen: Received ${userHighlights.size} highlights.")
        userHighlights.forEach {
            Timber.d(" -> Received Highlight: CFI=${it.cfi}, Text='${it.text.take(20)}...'")
        }
    }

    val context = LocalContext.current
    val textureBitmap = remember(activeTextureId) {
        activeTextureId?.let { id ->
            com.aryan.reader.epubreader.ReaderTexture.entries.find { it.id == id }?.resId?.let { resId ->
                androidx.compose.ui.graphics.ImageBitmap.imageResource(context.resources, resId)
            }
        }
    }

    val textureModifier = if (textureBitmap != null) {
        Modifier.drawBehind {
            val brush = androidx.compose.ui.graphics.ShaderBrush(
                androidx.compose.ui.graphics.ImageShader(textureBitmap, androidx.compose.ui.graphics.TileMode.Repeated, androidx.compose.ui.graphics.TileMode.Repeated)
            )
            drawRect(brush = brush, blendMode = BlendMode.Multiply, alpha = 0.6f)
        }
    } else Modifier

    BoxWithConstraints(modifier = modifier.fillMaxSize().background(effectiveBg).then(textureModifier)) {
        val textMeasurer = rememberTextMeasurer()
        val baseTextStyle = MaterialTheme.typography.bodyLarge

        var debouncedFontSizeMult by remember { mutableFloatStateOf(fontSizeMultiplier) }
        var debouncedLineHeightMult by remember { mutableFloatStateOf(lineHeightMultiplier) }
        var debouncedFontFamily by remember { mutableStateOf(fontFamily) }
        var debouncedTextAlign by remember { mutableStateOf(textAlign) }

        var anchorLocatorForReconfig by remember { mutableStateOf<Locator?>(null) }
        val currentPaginatorRef = remember { mutableStateOf<IPaginator?>(null) }

        val previousState = remember {
            arrayOf<Any>(this.constraints, isDarkTheme, effectiveBg, effectiveText)
        }

        if (previousState[0] != this.constraints ||
            previousState[1] != isDarkTheme ||
            previousState[2] != effectiveBg ||
            previousState[3] != effectiveText
        ) {
            val activePaginator = currentPaginatorRef.value
            if (activePaginator is BookPaginator) {
                val currentPage = pagerState.currentPage
                val locator = activePaginator.getLocatorForPage(currentPage)
                anchorLocatorForReconfig = locator

                Timber.tag("ThemeReconfig").d("""
            RECONFIG DETECTED
            - Reason: ${if (previousState[0] != this.constraints) "Constraints" else "Theme/Colors"}
            - Current Page: $currentPage
            - Saved Locator: $locator
        """.trimIndent())
            }
            previousState[0] = this.constraints
            previousState[1] = isDarkTheme
            previousState[2] = effectiveBg
            previousState[3] = effectiveText
        }

        val textStyle = remember(
            baseTextStyle, effectiveText,
            debouncedFontSizeMult,
            debouncedLineHeightMult,
            debouncedFontFamily
        ) {
            val adjustedFontSize = baseTextStyle.fontSize * debouncedFontSizeMult
            val adjustedLineHeight = adjustedFontSize * debouncedLineHeightMult

            baseTextStyle.copy(
                color = effectiveText,
                fontSize = adjustedFontSize,
                lineHeight = adjustedLineHeight,
                fontFamily = debouncedFontFamily,
                lineBreak = LineBreak.Paragraph,
                letterSpacing = TextUnit.Unspecified,
                platformStyle = PlatformTextStyle(includeFontPadding = false),
                lineHeightStyle = LineHeightStyle(
                    alignment = LineHeightStyle.Alignment.Proportional,
                    trim = LineHeightStyle.Trim.None
                )
            )
        }

        LaunchedEffect(fontSizeMultiplier, lineHeightMultiplier, fontFamily, textAlign) {
            if (fontSizeMultiplier != debouncedFontSizeMult || lineHeightMultiplier != debouncedLineHeightMult || fontFamily != debouncedFontFamily || textAlign != debouncedTextAlign) {
                Timber.d("Formatting changed. Waiting for debounce.")
                delay(400L)

                val activePaginator = currentPaginatorRef.value
                if (activePaginator is BookPaginator) {
                    val currentPage = pagerState.currentPage
                    val locator = activePaginator.getLocatorForPage(currentPage)
                    if (locator != null) {
                        anchorLocatorForReconfig = locator
                    }
                }

                debouncedFontSizeMult = fontSizeMultiplier
                debouncedLineHeightMult = lineHeightMultiplier
                debouncedFontFamily = fontFamily
                debouncedTextAlign = textAlign
                Timber.d("Debounce complete. Applying new format settings.")
            }
        }

        val userTextAlign = remember(debouncedTextAlign) {
            when (debouncedTextAlign) {
                ReaderTextAlign.JUSTIFY -> TextAlign.Justify
                ReaderTextAlign.LEFT -> TextAlign.Left
                ReaderTextAlign.DEFAULT -> null
            }
        }

        val density = LocalDensity.current
        val horizontalPadding = 16.dp
        val verticalPadding = 16.dp

        val textConstraints =
            remember(this.constraints, density, horizontalPadding, verticalPadding) {
                val horizontalPaddingPx = with(density) { horizontalPadding.roundToPx() }
                val verticalPaddingPx = with(density) { verticalPadding.roundToPx() }
                val finalConstraints = this.constraints.copy(
                    minWidth = 0,
                    maxWidth = this.constraints.maxWidth - (2 * horizontalPaddingPx),
                    minHeight = 0,
                    maxHeight = this.constraints.maxHeight - (2 * verticalPaddingPx)
                )
                finalConstraints
            }

        val coroutineScope = rememberCoroutineScope()
        val context = LocalContext.current
        val mathMLRenderer = remember { MathMLRenderer(context.applicationContext) }

        DisposableEffect(Unit) {
            onDispose {
                mathMLRenderer.destroy()
                Timber.d("PaginatedReaderScreen disposed, MathMLRenderer destroyed.")
            }
        }

        val effectiveInitialChapter =
            remember(initialChapterIndexInBook, anchorLocatorForReconfig) {
                anchorLocatorForReconfig?.chapterIndex ?: initialChapterIndexInBook ?: 0
            }
        val paginator = remember(book, textConstraints, isDarkTheme, textStyle, userTextAlign, effectiveBg, effectiveText) {
            val userAgentStylesheet = UserAgentStylesheet.default
            var allRules = OptimizedCssRules()
            val allFontFaces = mutableListOf<FontFaceInfo>()

            val uaResult = CssParser.parse(
                cssContent = userAgentStylesheet,
                cssPath = null,
                baseFontSizeSp = textStyle.fontSize.value,
                density = density.density,
                constraints = textConstraints,
                isDarkTheme = isDarkTheme,
                themeBackgroundColor = effectiveBg,
                themeTextColor = effectiveText
            )
            allRules = allRules.merge(uaResult.rules)
            allFontFaces.addAll(uaResult.fontFaces)

            book.css.forEach { (path, content) ->
                val bookCssResult = CssParser.parse(
                    cssContent = content,
                    cssPath = path,
                    baseFontSizeSp = textStyle.fontSize.value,
                    density = density.density,
                    constraints = textConstraints,
                    isDarkTheme = isDarkTheme,
                    themeBackgroundColor = effectiveBg,
                    themeTextColor = effectiveText
                )
                allRules = allRules.merge(bookCssResult.rules)
                allFontFaces.addAll(bookCssResult.fontFaces)
            }
            val fontFamilyMap = loadFontFamilies(
                fontFaces = allFontFaces, extractionPath = book.extractionBasePath
            )
            book.title
            val bookCacheDao =
                BookCacheDatabase.getDatabase(context.applicationContext).bookCacheDao()
            val proto = ProtoBuf { serializersModule = semanticBlockModule }

            val uniqueBookId = if (book.fileName.length > 20) book.fileName else book.title

            Timber.d("Recreating BookPaginator for ID: $uniqueBookId. TextAlign: $userTextAlign")
            Timber.tag("ReflowPaginationDiag").d("PaginatedReaderScreen: Instantiating BookPaginator. book.chaptersForPagination.size=${book.chaptersForPagination.size}, initialChapter=$effectiveInitialChapter")

            BookPaginator(
                coroutineScope = coroutineScope,
                chapters = book.chaptersForPagination,
                textMeasurer = textMeasurer,
                constraints = textConstraints,
                textStyle = textStyle,
                extractionBasePath = book.extractionBasePath,
                density = density,
                fontFamilyMap = fontFamilyMap,
                isDarkTheme = isDarkTheme,
                themeBackgroundColor = effectiveBg,
                themeTextColor = effectiveText,
                bookId = uniqueBookId,
                bookCacheDao = bookCacheDao,
                proto = proto,
                initialChapterToPaginate = effectiveInitialChapter,
                bookCss = book.css,
                userAgentStylesheet = userAgentStylesheet,
                allFontFaces = allFontFaces,
                context = context.applicationContext,
                mathMLRenderer = mathMLRenderer,
                userTextAlign = userTextAlign
            )
        }

        LaunchedEffect(paginator) {
            onPaginatorReady(paginator)
            currentPaginatorRef.value = paginator
        }

        LaunchedEffect(paginator) {
            if (anchorLocatorForReconfig != null) {
                Timber.tag("ThemeReconfig").d("Restoration Effect Triggered for Locator: $anchorLocatorForReconfig")

                snapshotFlow { paginator.isLoading }.filter { !it }.first()

                val targetLocator = anchorLocatorForReconfig
                if (targetLocator != null) {
                    val page = paginator.findPageForLocator(targetLocator)

                    Timber.tag("ThemeReconfig").d("""
                Restoration Progress:
                - Target Locator: $targetLocator
                - Paginator found Page: $page
                - Chapter Start Page: ${paginator.chapterStartPageIndices[targetLocator.chapterIndex]}
            """.trimIndent())

                    if (page != null) {
                        pagerState.scrollToPage(page)
                    } else {
                        val startPage = paginator.chapterStartPageIndices[targetLocator.chapterIndex]
                        if (startPage != null) {
                            Timber.tag("ThemeReconfig").w("Precise page not found, falling back to chapter start: $startPage")
                            pagerState.scrollToPage(startPage)
                        }
                    }
                    anchorLocatorForReconfig = null
                }
            }
        }

        var isLoading by remember { mutableStateOf(true) }
        var totalPageCount by remember { mutableIntStateOf(0) }
        var generation by remember { mutableIntStateOf(0) }

        LaunchedEffect(paginator) {
            launch { snapshotFlow { paginator.isLoading }.collect {
                Timber.tag("ReflowPaginationDiag").d("PaginatedReaderScreen: paginator.isLoading=$it")
                isLoading = it
            } }
            launch {
                snapshotFlow { paginator.totalPageCount }.collect { newTotalPageCount ->
                    Timber.tag("ReflowPaginationDiag").d("PaginatedReaderScreen: paginator.totalPageCount=$newTotalPageCount")
                    totalPageCount = newTotalPageCount
                }
            }
            launch { snapshotFlow { paginator.generation }.collect {
                Timber.tag("ReflowPaginationDiag").d("PaginatedReaderScreen: paginator.generation=$it")
                generation = it
            } }
        }

        LaunchedEffect(pagerState, paginator) {
            snapshotFlow { pagerState.currentPage }.debounce(500)
                .collectLatest { page -> paginator.onUserScrolledTo(page) }
        }

        LaunchedEffect(paginator, pagerState) {
            paginator.pageShiftRequest.collect { shiftAmount ->
                val newPage = pagerState.currentPage + shiftAmount
                pagerState.scrollToPage(newPage)
            }
        }

        val uiState = PaginatedReaderUiState(
            isLoading = isLoading, totalPageCount = totalPageCount, generation = generation
        )

        PaginatedReaderContent(
            uiState = uiState,
            pagerState = pagerState,
            isPageTurnAnimationEnabled = isPageTurnAnimationEnabled,
            effectiveBg = effectiveBg,
            searchQuery = searchQuery,
            ttsHighlightInfo = ttsHighlightInfo,
            textStyle = textStyle,
            horizontalPadding = horizontalPadding,
            verticalPadding = verticalPadding,
            onGetPage = { pageIndex -> paginator.getPageContent(pageIndex) },
            onGetChapterPath = { pageIndex -> paginator.getChapterPathForPage(pageIndex) },
            onGetChapterInfo = { pageIndex ->
                paginator.findChapterIndexForPage(pageIndex)?.let { chapterIndex ->
                    val chapter = book.chaptersForPagination.getOrNull(chapterIndex)
                    val estimatedPages = paginator.chapterPageCounts[chapterIndex]
                    if (chapter != null) {
                        Pair(chapter.title, estimatedPages)
                    } else {
                        null
                    }
                }
            },
            onLinkClick = { currentChapterPath, href, onNavComplete ->
                paginator.navigateToHref(currentChapterPath, href, onNavComplete)
            },
            onTap = onTap,
            isProUser = isProUser,
            isOss = isOss,
            onShowDictionaryUpsellDialog = onShowDictionaryUpsellDialog,
            onWordSelectedForAiDefinition = onWordSelectedForAiDefinition,
            onTranslate = onTranslate,
            onSearch = onSearch,
            onStartTtsFromSelection = onStartTtsFromSelection,
            userHighlights = userHighlights,
            onHighlightCreated = onHighlightCreated,
            onHighlightDeleted = onHighlightDeleted,
            isDarkTheme = isDarkTheme,
            activeHighlightPalette = activeHighlightPalette,
            onUpdatePalette = onUpdatePalette,
            effectiveText = effectiveText
        )
    }
}

private data class PaginatedMenuState(
    val rect: Rect, val onCopy: () -> Unit, val onHide: () -> Unit, val onSelectAll: (() -> Unit)?
)

private class CustomPaginatedTextToolbar(
    private val onShow: (Rect, () -> Unit, (() -> Unit)?) -> Unit, private val onHide: () -> Unit
) : TextToolbar {
    override fun showMenu(
        rect: Rect,
        onCopyRequested: (() -> Unit)?,
        onPasteRequested: (() -> Unit)?,
        onCutRequested: (() -> Unit)?,
        onSelectAllRequested: (() -> Unit)?
    ) {
        if (onCopyRequested != null) {
            onShow(rect, onCopyRequested, onSelectAllRequested)
        }
    }

    override val status: TextToolbarStatus = TextToolbarStatus.Hidden

    override fun hide() {
        onHide()
    }
}

private fun parseEmphasisAnnotation(annotation: String, defaultColor: Color): TextEmphasis {
    Timber.d("Parsing annotation string: '$annotation'")
    val map = annotation.split(';').filter { it.isNotBlank() }.associate {
        val (key, value) = it.split(':', limit = 2)
        key to value
    }
    val emphasis = TextEmphasis(
        style = map["s"],
        fill = map["f"],
        color = map["c"]?.toULongOrNull()?.let { Color(it) } ?: defaultColor,
        position = map["p"])
    Timber.d("Parsed annotation to object: $emphasis")
    return emphasis
}

private fun findFuzzyMatch(source: String, target: String, ignoreCase: Boolean = true): IntRange? {
    if (target.isBlank()) return null
    val targetWords = target.split("\\s+".toRegex()).filter { it.isNotEmpty() }
    if (targetWords.isEmpty()) return null

    var searchStart = 0
    while (searchStart < source.length) {
        val firstIdx = source.indexOf(targetWords[0], searchStart, ignoreCase = ignoreCase)
        if (firstIdx == -1) return null

        var currentIdx = firstIdx + targetWords[0].length
        var allMatch = true

        for (i in 1 until targetWords.size) {
            while (currentIdx < source.length && source[currentIdx].isWhitespace()) {
                currentIdx++
            }
            if (currentIdx >= source.length) {
                allMatch = false
                break
            }

            val word = targetWords[i]
            if (source.regionMatches(currentIdx, word, 0, word.length, ignoreCase = ignoreCase)) {
                currentIdx += word.length
            } else {
                allMatch = false
                break
            }
        }

        if (allMatch) return firstIdx until currentIdx
        searchStart = firstIdx + 1
    }
    return null
}

private fun getHighlightOffsetsInBlock(
    block: TextContentBlock, highlight: UserHighlight
): IntRange? {
    if (block.cfi == null) return null

    val blockPath = CfiUtils.getPath(block.cfi!!)
    val parts = highlight.cfi.split('|')
    val startCfi = parts.firstOrNull() ?: highlight.cfi
    val endCfi = parts.lastOrNull()

    @Suppress("REDUNDANT_ELSE_IN_WHEN") val blockStartAbs = when (block) {
        is ParagraphBlock -> block.startCharOffsetInSource
        is HeaderBlock -> block.startCharOffsetInSource
        is QuoteBlock -> block.startCharOffsetInSource
        is ListItemBlock -> block.startCharOffsetInSource
        else -> 0
    }

    Timber.d(
        "getHighlightOffsetsInBlock: Checking Block=${block.cfi} (AbsStart=$blockStartAbs) against Highlight=${highlight.cfi}"
    )

    val relevantPart = parts.find { cfiPart ->
        val highlightPath = CfiUtils.getPath(cfiPart)

        if (highlightPath.startsWith(blockPath)) return@find true

        val highlightSegments = highlightPath.split('/').filter { it.isNotEmpty() }
        val blockSegments = blockPath.split('/').filter { it.isNotEmpty() }

        if (highlightSegments.size > blockSegments.size) {
            val pathWithoutFirst = "/" + highlightSegments.drop(1).joinToString("/")
            if (pathWithoutFirst.startsWith(blockPath)) return@find true
        }

        if (highlightSegments.isNotEmpty() && blockSegments.isNotEmpty()) {
            if (highlightSegments[0] != blockSegments[0]) {
                val highlightTail = highlightSegments.drop(1)
                val blockTail = blockSegments.drop(1)
                if (blockTail.isNotEmpty() && highlightTail.size >= blockTail.size) {
                    var match = true
                    for (i in blockTail.indices) {
                        if (blockTail[i] != highlightTail[i]) {
                            match = false
                            break
                        }
                    }
                    if (match) return@find true
                }
            }
        }
        false
    }

    if (relevantPart != null) {
        Timber.d(
            " -> Block ${block.cfi} matches specific part of multipart highlight: $relevantPart"
        )
    }

    var isAfterStart = false
    var isBeforeEnd = true

    if (relevantPart == null) {
        if (startCfi.isNotEmpty()) {
            try {
                if (CfiUtils.compare(block.cfi!!, startCfi) > 0) {
                    isAfterStart = true
                }
            } catch (_: Exception) {
            }
        }

        if (endCfi != null && endCfi != startCfi) {
            try {
                val endPath = CfiUtils.getPath(endCfi)
                val cmp = CfiUtils.compare(blockPath, endPath)
                Timber.d(" -> Comparing BlockPath ($blockPath) vs EndPath ($endPath). Result: $cmp")
                if (CfiUtils.compare(blockPath, endPath) > 0) {
                    isBeforeEnd = false
                }
            } catch (_: Exception) {
            }
        }
    }

    Timber.d(" -> relevantPart=$relevantPart, isAfterStart=$isAfterStart, isBeforeEnd=$isBeforeEnd")

    if (relevantPart == null && (!isAfterStart || !isBeforeEnd)) {
        return null
    }

    val blockText = block.content.text
    val highlightText = highlight.text

    if (blockText.isEmpty() || highlightText.isEmpty()) return null
    if (highlightText.contains(blockText, ignoreCase = false)) return 0 until blockText.length
    if (highlightText.contains(blockText, ignoreCase = true)) return 0 until blockText.length

    var startIndex = blockText.indexOf(highlightText, ignoreCase = false)
    if (startIndex == -1) {
        startIndex = blockText.indexOf(highlightText, ignoreCase = true)
    }

    if (relevantPart != null) {
        fun arePathsEquivalent(path1: String, path2: String): Boolean {
            val p1 = CfiUtils.getPath(path1).split('/').filter { it.isNotEmpty() }
            val p2 = CfiUtils.getPath(path2).split('/').filter { it.isNotEmpty() }

            if (p1 == p2) return true

            if (p1.size == p2.size && p1.isNotEmpty()) {
                return p1.drop(1) == p2.drop(1)
            }
            return false
        }

        val startMatches = arePathsEquivalent(startCfi, block.cfi!!)
        val endMatches = if (endCfi != null) arePathsEquivalent(endCfi, block.cfi!!) else false

        Timber.d(" -> Path Equivalence: StartMatches=$startMatches, EndMatches=$endMatches")

        if (startMatches || endMatches) {
            var s = 0
            var e = blockText.length

            if (startMatches) {
                val absOffset = CfiUtils.getOffset(startCfi)
                val relOffset = absOffset - blockStartAbs

                if (relOffset < 0) {
                    s = 0
                } else {
                    val safeStart = (relOffset - 50).coerceAtLeast(0)
                    val safeEnd = (relOffset + 50).coerceAtMost(blockText.length)

                    if (safeStart < safeEnd) {
                        val windowText = blockText.substring(safeStart, safeEnd)
                        val prefix = highlightText.trim().take(20).trim()

                        var snapped = false
                        if (prefix.isNotEmpty()) {
                            val matches = mutableListOf<Int>()
                            var idx = windowText.indexOf(prefix, ignoreCase = true)
                            while (idx != -1) {
                                matches.add(idx)
                                idx = windowText.indexOf(prefix, idx + 1, ignoreCase = true)
                            }

                            if (matches.isNotEmpty()) {
                                val targetRel = relOffset - safeStart
                                val bestRel = matches.minByOrNull { abs(it - targetRel) }!!
                                val newS = safeStart + bestRel
                                Timber.d(
                                    "Snapped start offset from rel $relOffset to $newS based on prefix '$prefix'"
                                )
                                s = newS
                                snapped = true
                            }
                        }

                        if (!snapped) {
                            s = relOffset
                        }
                    } else {
                        s = relOffset
                    }
                }
            }

            if (endMatches) {
                val absOffset = CfiUtils.getOffset(endCfi!!)
                val relOffset = absOffset - blockStartAbs

                Timber.d(
                    " -> EndCFI Match. AbsOffset: $absOffset. RelOffset: $relOffset. Block Length: ${blockText.length}"
                )

                e = if (relOffset > blockText.length) {
                    blockText.length
                } else {
                    relOffset
                }
            }

            s = s.coerceIn(0, blockText.length)
            e = e.coerceIn(0, blockText.length)

            if (s < e) {
                Timber.d("Fallback to CFI offsets for block ${block.cfi}. Range: $s..$e")
                return s until e
            } else {
                Timber.w(
                    " -> Invalid Range detected (likely highlight is on other split part): $s..$e"
                )
                return null
            }
        }
    }

    if (startIndex >= 0) {
        return startIndex until (startIndex + highlightText.length)
    }

    if (relevantPart == null) {
        @Suppress("KotlinConstantConditions") if (isAfterStart) {
            val normBlock = blockText.filter { !it.isWhitespace() }
            val normHighlight = highlightText.filter { !it.isWhitespace() }
            if (normHighlight.contains(normBlock, ignoreCase = true)) {
                return 0 until blockText.length
            }
        }
    }

    val match = findFuzzyMatch(blockText, highlightText)
    if (match != null) return match

    if (relevantPart != null) {
        Timber.d(
            "Failed to match highlight text in block despite CFI match. " + "BlockCfi=${block.cfi}, HighlightCfi=${highlight.cfi}. "
        )
    }

    return null
}

@Composable
private fun TextWithEmphasis(
    text: AnnotatedString,
    modifier: Modifier = Modifier,
    style: TextStyle,
    @Suppress("unused") textMeasurer: TextMeasurer,
    onLinkClick: (String) -> Unit,
    onGeneralTap: (Offset) -> Unit,
    block: TextContentBlock,
    userHighlights: List<UserHighlight>,
    activeSelection: PaginatedSelection?,
    @Suppress("unused") onSelectionChange: (PaginatedSelection?) -> Unit,
    onHighlightClick: (UserHighlight, Rect) -> Unit,
    @Suppress("unused") isDarkTheme: Boolean,
    onRegisterLayout: ((TextLayoutResult, LayoutCoordinates) -> Unit)? = null
) {
    var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
    val viewConfiguration = LocalViewConfiguration.current
    var layoutCoordinates by remember { mutableStateOf<LayoutCoordinates?>(null) }
    val scope = rememberCoroutineScope()
    var pressedHighlightCfi by remember { mutableStateOf<String?>(null) }

    val customDrawer = Modifier.drawBehind {
        textLayoutResult?.let { layoutResult ->
            if (activeSelection != null && activeSelection.blockIndex == block.blockIndex) {
                val path = layoutResult.getPathForRange(
                    activeSelection.startOffset, activeSelection.endOffset
                )
                drawPath(path, Color(0xFF1976D2).copy(alpha = 0.3f))
            }

            if (block.cfi != null && userHighlights.isNotEmpty()) {
                userHighlights.forEach { highlight ->
                    val range = getHighlightOffsetsInBlock(block, highlight)

                    if (range != null) {
                        try {
                            val path = layoutResult.getPathForRange(
                                range.first, range.last + 1
                            )

                            drawPath(
                                path,
                                highlight.color.color.copy(alpha = 0.4f),
                                blendMode = BlendMode.SrcOver
                            )

                            if (highlight.cfi == pressedHighlightCfi) {
                                drawPath(
                                    path,
                                    Color.Black.copy(alpha = 0.1f),
                                    blendMode = BlendMode.SrcOver
                                )
                            }
                        } catch (_: Exception) { }
                    }
                }
            }

            val emphasisAnnotations = text.getStringAnnotations("TextEmphasis", 0, text.length)
            if (emphasisAnnotations.isNotEmpty()) {
                emphasisAnnotations.forEach { annotation ->
                    val emphasis = parseEmphasisAnnotation(annotation.item, style.color)
                    val markColor = if (emphasis.color.isSpecified) emphasis.color else style.color
                    val markSize = layoutResult.layoutInput.style.fontSize.toPx() * 0.3f
                    for (offset in annotation.start until annotation.end) {
                        if (offset >= text.text.length || text.text[offset].isWhitespace()) continue
                        try {
                            val boundingBox = layoutResult.getBoundingBox(offset)
                            val center = Offset(
                                boundingBox.center.x,
                                if (emphasis.position == "under") boundingBox.bottom + markSize * 0.1f
                                else boundingBox.top - markSize * 0.1f
                            )
                            drawCircle(markColor, markSize / 2, center, style = Stroke(1f))
                        } catch (_: Exception) {
                        }
                    }
                }
            }
        }
    }

    fun getHighlightAt(offset: Offset, layout: TextLayoutResult): Pair<UserHighlight, Rect>? {
        if (block.cfi == null) return null

        // Optimization: Quick bounds check
        val charOffset = layout.getOffsetForPosition(offset)
        val lineIndex = layout.getLineForOffset(charOffset)
        val lineLeft = layout.getLineLeft(lineIndex)
        val lineRight = layout.getLineRight(lineIndex)
        if (offset.x < minOf(lineLeft, lineRight) - 50 || offset.x > maxOf(
                lineLeft, lineRight
            ) + 50
        ) {
            return null
        }

        // Iterate highlights reversed (topmost first)
        for (highlight in userHighlights.reversed()) {
            val range = getHighlightOffsetsInBlock(block, highlight) ?: continue

            if (charOffset in range) {
                val path = layout.getPathForRange(range.first, range.last)
                val bounds = path.getBounds()
                return highlight to bounds
            }
        }
        return null
    }

    Text(text = text, style = style, modifier = modifier
        .onGloballyPositioned {
            layoutCoordinates = it
            if (textLayoutResult != null && block.cfi != null) {
                onRegisterLayout?.invoke(textLayoutResult!!, it)
            }
        }
        .then(customDrawer)
        .pointerInput(userHighlights, text) {
            awaitEachGesture {
                val down = awaitFirstDown(
                    pass = PointerEventPass.Initial, requireUnconsumed = false
                )
                val layout = textLayoutResult
                if (layout != null) {
                    val hit = getHighlightAt(down.position, layout)
                    if (hit != null) {
                        down.consume()
                        val startPosition = down.position
                        var dragDistance = 0f
                        var isFinished = false

                        val longPressJob = scope.launch {
                            delay(500)
                            pressedHighlightCfi = hit.first.cfi
                        }

                        try {
                            while (true) {
                                val event = awaitPointerEvent(
                                    pass = PointerEventPass.Initial
                                )
                                val change = event.changes.firstOrNull {
                                    it.id == down.id
                                }
                                if (change == null) {
                                    longPressJob.cancel()
                                    pressedHighlightCfi = null
                                    break
                                }
                                change.consume()
                                if (change.pressed) {
                                    dragDistance = (change.position - startPosition).getDistance()
                                    if (dragDistance >= viewConfiguration.touchSlop) {
                                        longPressJob.cancel()
                                        pressedHighlightCfi = null
                                    }
                                } else {
                                    isFinished = true
                                    longPressJob.cancel()
                                    pressedHighlightCfi = null
                                    break
                                }
                            }
                        } catch (_: Exception) {
                            longPressJob.cancel()
                            pressedHighlightCfi = null
                        }

                        if (isFinished && dragDistance < viewConfiguration.touchSlop) {
                            val (highlight, localRect) = hit
                            val globalRect = layoutCoordinates?.let { coords ->
                                if (coords.isAttached) {
                                    val topLeft = coords.localToWindow(
                                        localRect.topLeft
                                    )
                                    val bottomRight = coords.localToWindow(
                                        localRect.bottomRight
                                    )
                                    Rect(topLeft, bottomRight)
                                } else null
                            } ?: localRect
                            onHighlightClick(highlight, globalRect)
                        }
                    }
                }
            }
        }
        .pointerInput(text) {
            detectTapGestures(
                onTap = { offset ->
                    textLayoutResult?.let { layout ->
                        val charOffset = layout.getOffsetForPosition(offset)
                        val urlAnnotation = text.getStringAnnotations(
                            "URL", charOffset, charOffset
                        ).firstOrNull()
                        if (urlAnnotation != null) onLinkClick(urlAnnotation.item)
                        else onGeneralTap(offset)
                    }
                })
        }, onTextLayout = {
        textLayoutResult = it
        if (layoutCoordinates != null && block.cfi != null) {
            onRegisterLayout?.invoke(it, layoutCoordinates!!)
        }
    })
}

@SuppressLint("BinaryOperationInTimber")
private fun checkLayoutMismatch(
    blockIndex: Int,
    blockType: String,
    expectedHeight: Int,
    actualHeight: Int,
    textSnippet: String,
    @Suppress("SameParameterValue") tolerance: Int = 2
) {
    if (expectedHeight == 0) {
        Timber.tag("PAGINATION_MISMATCH").w("Block #$blockIndex ($blockType) has expectedHeight=0. Skipping check. Text: '$textSnippet'")
        return
    }

    if (actualHeight > expectedHeight + tolerance) {
        val diff = actualHeight - expectedHeight
        Timber.tag("PAGINATION_MISMATCH").e(
            "OVERFLOW DETECTED! Block #$blockIndex ($blockType)\n" +
                    " -> Expected: ${expectedHeight}px\n" +
                    " -> Actual:   ${actualHeight}px\n" +
                    " -> Diff:     +${diff}px\n" +
                    " -> Content:  '$textSnippet'"
        )
    }
}

@SuppressLint("UnusedBoxWithConstraintsScope")
@OptIn(ExperimentalFoundationApi::class)
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
internal fun PaginatedReaderContent(
    uiState: PaginatedReaderUiState,
    pagerState: PagerState,
    isPageTurnAnimationEnabled: Boolean,
    effectiveBg: Color,
    effectiveText: Color,
    searchQuery: String,
    ttsHighlightInfo: TtsHighlightInfo?,
    textStyle: TextStyle,
    horizontalPadding: Dp,
    verticalPadding: Dp,
    onGetPage: (Int) -> Page?,
    onGetChapterPath: (Int) -> String?,
    onLinkClick: (currentChapterPath: String, href: String, onNavComplete: (Int) -> Unit) -> Unit,
    onTap: (Offset?) -> Unit,
    isProUser: Boolean,
    isOss: Boolean,
    onShowDictionaryUpsellDialog: () -> Unit,
    onWordSelectedForAiDefinition: (String) -> Unit,
    onTranslate: (String) -> Unit,
    onSearch: (String) -> Unit,
    onStartTtsFromSelection: (String, Int) -> Unit,
    onGetChapterInfo: (Int) -> Pair<String, Int?>?,
    userHighlights: List<UserHighlight>,
    onHighlightCreated: (String, String, String) -> Unit,
    onHighlightDeleted: (String) -> Unit,
    activeHighlightPalette: List<HighlightColor>,
    onUpdatePalette: (Int, HighlightColor) -> Unit,
    isDarkTheme: Boolean
) {
    val coroutineScope = rememberCoroutineScope()
    val density = LocalDensity.current
    var menuState by remember { mutableStateOf<PaginatedMenuState?>(null) }
    var showExternalLinkDialog by remember { mutableStateOf<String?>(null) }
    val context = LocalContext.current
    val imageLoader = context.imageLoader
    val textMeasurer = rememberTextMeasurer()
    var activeSelection by remember { mutableStateOf<PaginatedSelection?>(null) }
    var activeHighlightForMenu by remember { mutableStateOf<Pair<UserHighlight, Rect>?>(null) }
    val blockLayoutMap = remember {
        androidx.compose.runtime.mutableStateMapOf<String, Triple<TextLayoutResult, LayoutCoordinates, Int>>()
    }
    var showColorPickerDialog by remember { mutableStateOf<Int?>(null) }
    var showPaletteManager by remember { mutableStateOf(false) }

    if (showExternalLinkDialog != null) {
        val urlToShow = showExternalLinkDialog!!
        AlertDialog(
            onDismissRequest = { showExternalLinkDialog = null },
            title = { Text("External Link") },
            text = {
                Text(
                    "You clicked on an external link:\n\n$urlToShow\n\nWhat would you like to do?"
                )
            },
            confirmButton = {
                Row(horizontalArrangement = Arrangement.End) {
                    TextButton(
                        onClick = {
                            val clipboard =
                                context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
                            val clip = ClipData.newPlainText("Copied Link", urlToShow)
                            clipboard.setPrimaryClip(clip)
                            showExternalLinkDialog = null
                        }) { Text("Copy") }
                    TextButton(
                        onClick = {
                            val intent = Intent(Intent.ACTION_VIEW, urlToShow.toUri())
                            try {
                                context.startActivity(intent)
                            } catch (e: ActivityNotFoundException) {
                                Timber.e(
                                    e, "No activity found to handle intent for URL: $urlToShow"
                                )
                                Toast.makeText(
                                    context, "No browser found to open the link.", Toast.LENGTH_LONG
                                ).show()
                            }
                            showExternalLinkDialog = null
                        }) { Text("Open") }
                }
            },
            dismissButton = {
                TextButton(onClick = { showExternalLinkDialog = null }) { Text("Cancel") }
            })
    }

    val textToolbar = remember {
        CustomPaginatedTextToolbar(onShow = { rect, onCopy, onSelectAll ->
            if (activeHighlightForMenu == null) {
                menuState = PaginatedMenuState(
                    rect, onCopy, onHide = { menuState = null }, onSelectAll
                )
            }
        }, onHide = { menuState = null })
    }

    var pageTurnTouchY by remember { mutableStateOf<Float?>(null) }

    if (uiState.isLoading) {
        Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            CircularProgressIndicator()
        }
    } else {
        Timber.tag("ReflowPaginationDiag").d("PaginatedReaderContent: isLoading=false, totalPageCount=${uiState.totalPageCount}")
        if (uiState.totalPageCount > 0) {
            uiState.generation

            val realClipboard: Clipboard = LocalClipboard.current
            var isForDictionary by remember { mutableStateOf(false) }
            var isForHighlight by remember { mutableStateOf(false) }
            var capturedTextForAction by remember { mutableStateOf<String?>(null) }

            val dictionaryClipboard = remember(realClipboard) {
                object : Clipboard {
                    override val nativeClipboard: ClipboardManager
                        get() = realClipboard.nativeClipboard

                    override suspend fun getClipEntry(): ClipEntry? = realClipboard.getClipEntry()

                    override suspend fun setClipEntry(clipEntry: ClipEntry?) {
                        val text = clipEntry?.clipData?.getItemAt(0)?.text?.toString()
                        Timber.d(
                            "Clipboard intercept: setClipEntry called. Text: '$text', isForHighlight: $isForHighlight"
                        )
                        capturedTextForAction = text

                        if (isForDictionary) {
                            if (!text.isNullOrBlank()) {
                                onWordSelectedForAiDefinition(text)
                            }
                        }  else if (isForHighlight) {
                            // Do not copy to real clipboard
                        } else {
                            realClipboard.setClipEntry(clipEntry)
                        }
                    }
                }
            }

            CompositionLocalProvider(
                LocalTextToolbar provides textToolbar, LocalClipboard provides dictionaryClipboard
            ) {
                HorizontalPager(
                    state = pagerState,
                    modifier = Modifier
                        .fillMaxSize()
                        .pointerInput(Unit) {
                            awaitPointerEventScope {
                                while (true) {
                                    val event = awaitPointerEvent(PointerEventPass.Initial)
                                    val down = event.changes.firstOrNull { it.pressed }
                                    if (down != null) {
                                        pageTurnTouchY = down.position.y
                                    }
                                }
                            }
                        },
                    beyondViewportPageCount = 1
                ) { pageIndex ->
                    val pageOffset = (pageIndex - pagerState.currentPage) - pagerState.currentPageOffsetFraction
                    val zIndex = -pageOffset

                    val pageModifier = if (isPageTurnAnimationEnabled) {
                        Modifier
                            .zIndex(zIndex)
                            .realisticBookPage(pagerState, pageIndex, effectiveBg, isDarkTheme, pageTurnTouchY)
                    } else {
                        Modifier
                    }

                    var pageContent by remember { mutableStateOf<Page?>(null) }
                    var currentChapterPath by remember { mutableStateOf<String?>(null) }

                    LaunchedEffect(pageIndex, uiState.generation) {
                        Timber.tag("ReflowPaginationDiag").d("PaginatedReaderContent: Fetching page $pageIndex content. generation=${uiState.generation}")
                        pageContent = onGetPage(pageIndex)
                        Timber.tag("ReflowPaginationDiag").d("PaginatedReaderContent: Fetched page $pageIndex content. isNull=${pageContent == null}, blocks=${pageContent?.content?.size}")
                        onGetChapterPath(pageIndex)?.let { currentChapterPath = it }
                    }

                    Box(modifier = Modifier.fillMaxSize().then(pageModifier)) {
                        SelectionContainer(modifier = Modifier.fillMaxSize()) {
                            Box(modifier = Modifier
                                .fillMaxSize()
                                .pointerInput(Unit) {
                                    detectTapGestures(
                                        onTap = { offset ->
                                            Timber.d(
                                                "Tap detected on empty page area."
                                            )
                                            menuState?.onHide?.invoke()
                                            onTap(offset)
                                        })
                                }
                                .padding(
                                    horizontal = horizontalPadding, vertical = verticalPadding
                                ), contentAlignment = Alignment.TopStart) {
                                if (pageContent != null) {
                                    val onGeneralTapCallback: (Offset) -> Unit = { offset ->
                                        menuState?.onHide?.invoke()
                                        onTap(offset)
                                    }
                                    val onLinkClickCallback: (String) -> Unit = { href ->
                                        Timber.d("Link clicked: $href")
                                        if (href.startsWith("http://") || href.startsWith("https://")) {
                                            showExternalLinkDialog = href
                                        } else {
                                            currentChapterPath?.let { path ->
                                                onLinkClick(path, href) { targetPageIndex ->
                                                    coroutineScope.launch {
                                                        pagerState.scrollToPage(targetPageIndex)
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    Column(modifier = Modifier.fillMaxSize()) {
                                        val searchHighlightColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.4f)
                                        val ttsHighlightColor = MaterialTheme.colorScheme.secondary.copy(alpha = 0.5f)

                                        pageContent!!.content.forEach { block ->
                                            val marginModifier = Modifier.padding(
                                                top = block.style.margin.top.coerceAtLeast(0.dp),
                                                bottom = block.style.margin.bottom.coerceAtLeast(0.dp)
                                            )

                                            val alignModifier = if (block.style.horizontalAlign == "center") {
                                                Modifier.align(Alignment.CenterHorizontally)
                                            } else {
                                                Modifier.padding(
                                                    start = block.style.margin.left.coerceAtLeast(0.dp),
                                                    end = block.style.margin.right.coerceAtLeast(0.dp)
                                                )
                                            }

                                            val widthModifier = if (block.style.width != Dp.Unspecified) {
                                                Modifier.width(block.style.width)
                                            } else {
                                                Modifier.fillMaxWidth()
                                            }

                                            val styleModifier = alignModifier
                                                .then(if (block.style.horizontalAlign == "center") widthModifier else Modifier)
                                                .drawCssBorders(
                                                    blockStyle = block.style,
                                                    density = density
                                                )

                                            val diagnosticModifier = Modifier
                                                .onGloballyPositioned { coordinates ->
                                                    val actualHeight = coordinates.size.height
                                                    if (block.expectedHeight > 0) {
                                                        val snippet = when(block) {
                                                            is ParagraphBlock -> block.content.text.take(50)
                                                            is HeaderBlock -> block.content.text.take(50)
                                                            is QuoteBlock -> block.content.text.take(50)
                                                            is ListItemBlock -> block.content.text.take(50)
                                                            is TextContentBlock -> block.content.text.take(50)
                                                            else -> "Non-text content"
                                                        }

                                                        checkLayoutMismatch(
                                                            blockIndex = block.blockIndex,
                                                            blockType = block::class.simpleName ?: "Block",
                                                            expectedHeight = block.expectedHeight,
                                                            actualHeight = actualHeight,
                                                            textSnippet = snippet,
                                                            tolerance = 2
                                                        )
                                                    }
                                                }
                                                .then(marginModifier)
                                                .then(styleModifier)

                                            Box(modifier = diagnosticModifier) {
                                                val paddingModifier = Modifier.padding(
                                                    start = block.style.padding.left.coerceAtLeast(0.dp) + (block.style.borderLeft?.width ?: 0.dp),
                                                    top = block.style.padding.top.coerceAtLeast(0.dp) + (block.style.borderTop?.width ?: 0.dp),
                                                    end = block.style.padding.right.coerceAtLeast(0.dp) + (block.style.borderRight?.width ?: 0.dp),
                                                    bottom = block.style.padding.bottom.coerceAtLeast(0.dp) + (block.style.borderBottom?.width ?: 0.dp)
                                                ).then(
                                                    if (block.style.horizontalAlign != "center") widthModifier else Modifier.fillMaxWidth()
                                                )

                                                @Suppress("DEPRECATION") when (block) {
                                                    is ParagraphBlock -> {
                                                        val paragraphStyle = textStyle.copy(
                                                            textAlign = block.textAlign ?: textStyle.textAlign
                                                        )
                                                        val searchHighlighted = highlightQueryInText(
                                                            block.content,
                                                            searchQuery,
                                                            searchHighlightColor
                                                        )
                                                        val finalContent =
                                                            if (ttsHighlightInfo != null && block.cfi == ttsHighlightInfo.cfi) {
                                                                buildAnnotatedString {
                                                                    append(searchHighlighted)

                                                                    // Define absolute ranges
                                                                    val blockStartAbs =
                                                                        block.startCharOffsetInSource
                                                                    val blockEndAbs =
                                                                        block.startCharOffsetInSource + searchHighlighted.length
                                                                    val highlightStartAbs =
                                                                        ttsHighlightInfo.offset
                                                                    val highlightEndAbs =
                                                                        ttsHighlightInfo.offset + ttsHighlightInfo.text.length

                                                                    // Calculate intersection
                                                                    val intersectionStartAbs = maxOf(
                                                                        blockStartAbs, highlightStartAbs
                                                                    )
                                                                    val intersectionEndAbs = minOf(
                                                                        blockEndAbs, highlightEndAbs
                                                                    )

                                                                    // Check for overlap and apply
                                                                    // style
                                                                    if (intersectionStartAbs < intersectionEndAbs) {
                                                                        val highlightStartRelative =
                                                                            intersectionStartAbs - blockStartAbs
                                                                        val highlightEndRelative =
                                                                            intersectionEndAbs - blockStartAbs
                                                                        addStyle(
                                                                            style = SpanStyle(
                                                                                background = ttsHighlightColor
                                                                            ),
                                                                            start = highlightStartRelative,
                                                                            end = highlightEndRelative
                                                                        )
                                                                    }
                                                                }
                                                            } else {
                                                                searchHighlighted
                                                            }

                                                        @Suppress("UnusedVariable", "Unused") val diagnosticModifier =
                                                            if (block.textAlign == TextAlign.Justify) {
                                                                Modifier.onGloballyPositioned { coordinates ->
                                                                    val width = coordinates.size.width
                                                                    Timber.d(
                                                                        """
                                                                [UI Render]
                                                                Block Index: ${block.blockIndex}
                                                                Text Start: ${
                                                                            block.content.text.take(
                                                                                20
                                                                            )
                                                                        }...
                                                                Actual Render Width Px: $width
                                                                ------------------------------------------------
                                                            """.trimIndent()
                                                                    )
                                                                }
                                                            } else {
                                                                Modifier
                                                            }

                                                        TextWithEmphasis(
                                                            text = finalContent,
                                                            style = paragraphStyle,
                                                            modifier = paddingModifier,
                                                            textMeasurer = textMeasurer,
                                                            onLinkClick = onLinkClickCallback,
                                                            onGeneralTap = onGeneralTapCallback,
                                                            block = block,
                                                            userHighlights = userHighlights,
                                                            activeSelection = activeSelection,
                                                            onSelectionChange = { sel ->
                                                                activeSelection = sel
                                                            },
                                                            onHighlightClick = { highlight, rect ->
                                                                activeHighlightForMenu =
                                                                    highlight to rect
                                                                activeSelection = null
                                                                menuState = null
                                                            },
                                                            isDarkTheme = isDarkTheme,
                                                            onRegisterLayout = { layout, coords ->
                                                                if (block.cfi != null) blockLayoutMap[block.cfi] =
                                                                    Triple(
                                                                        layout,
                                                                        coords,
                                                                        block.startCharOffsetInSource
                                                                    )
                                                            })
                                                    }

                                                    is HeaderBlock -> {
                                                        val style = textStyle.copy(
                                                            fontWeight = FontWeight.Bold,
                                                            textAlign = block.textAlign ?: textStyle.textAlign
                                                        )
                                                        val searchHighlighted = highlightQueryInText(
                                                            block.content,
                                                            searchQuery,
                                                            searchHighlightColor
                                                        )
                                                        val finalContent =
                                                            if (ttsHighlightInfo != null && block.cfi == ttsHighlightInfo.cfi) {
                                                                buildAnnotatedString {
                                                                    append(searchHighlighted)

                                                                    val blockStartAbs =
                                                                        block.startCharOffsetInSource
                                                                    val blockEndAbs =
                                                                        block.startCharOffsetInSource + searchHighlighted.length
                                                                    val highlightStartAbs =
                                                                        ttsHighlightInfo.offset
                                                                    val highlightEndAbs =
                                                                        ttsHighlightInfo.offset + ttsHighlightInfo.text.length

                                                                    val intersectionStartAbs = maxOf(
                                                                        blockStartAbs, highlightStartAbs
                                                                    )
                                                                    val intersectionEndAbs = minOf(
                                                                        blockEndAbs, highlightEndAbs
                                                                    )

                                                                    if (intersectionStartAbs < intersectionEndAbs) {
                                                                        val highlightStartRelative =
                                                                            intersectionStartAbs - blockStartAbs
                                                                        val highlightEndRelative =
                                                                            intersectionEndAbs - blockStartAbs
                                                                        addStyle(
                                                                            style = SpanStyle(
                                                                                background = ttsHighlightColor
                                                                            ),
                                                                            start = highlightStartRelative,
                                                                            end = highlightEndRelative
                                                                        )
                                                                    }
                                                                }
                                                            } else {
                                                                searchHighlighted
                                                            }
                                                        TextWithEmphasis(
                                                            text = finalContent,
                                                            style = style,
                                                            modifier = paddingModifier,
                                                            textMeasurer = textMeasurer,
                                                            onLinkClick = onLinkClickCallback,
                                                            onGeneralTap = onGeneralTapCallback,
                                                            block = block,
                                                            userHighlights = userHighlights,
                                                            activeSelection = activeSelection,
                                                            onSelectionChange = { sel ->
                                                                activeSelection = sel
                                                            },
                                                            onHighlightClick = { highlight, rect ->
                                                                activeHighlightForMenu =
                                                                    highlight to rect
                                                                activeSelection = null
                                                                menuState = null
                                                            },
                                                            isDarkTheme = isDarkTheme,
                                                            onRegisterLayout = { layout, coords ->
                                                                if (block.cfi != null) blockLayoutMap[block.cfi] =
                                                                    Triple(
                                                                        layout,
                                                                        coords,
                                                                        block.startCharOffsetInSource
                                                                    )
                                                            })
                                                    }

                                                    is QuoteBlock -> {
                                                        val quoteStyle = textStyle.copy(
                                                            textAlign = block.textAlign ?: textStyle.textAlign
                                                        )
                                                        val quoteModifier =
                                                            paddingModifier.padding(start = 16.dp)
                                                        val searchHighlighted = highlightQueryInText(
                                                            block.content,
                                                            searchQuery,
                                                            searchHighlightColor
                                                        )
                                                        val finalContent =
                                                            if (ttsHighlightInfo != null && block.cfi == ttsHighlightInfo.cfi) {
                                                                buildAnnotatedString {
                                                                    append(searchHighlighted)

                                                                    val blockStartAbs =
                                                                        block.startCharOffsetInSource
                                                                    val blockEndAbs =
                                                                        block.startCharOffsetInSource + searchHighlighted.length
                                                                    val highlightStartAbs =
                                                                        ttsHighlightInfo.offset
                                                                    val highlightEndAbs =
                                                                        ttsHighlightInfo.offset + ttsHighlightInfo.text.length

                                                                    val intersectionStartAbs = maxOf(
                                                                        blockStartAbs, highlightStartAbs
                                                                    )
                                                                    val intersectionEndAbs = minOf(
                                                                        blockEndAbs, highlightEndAbs
                                                                    )

                                                                    if (intersectionStartAbs < intersectionEndAbs) {
                                                                        val highlightStartRelative =
                                                                            intersectionStartAbs - blockStartAbs
                                                                        val highlightEndRelative =
                                                                            intersectionEndAbs - blockStartAbs
                                                                        addStyle(
                                                                            style = SpanStyle(
                                                                                background = ttsHighlightColor
                                                                            ),
                                                                            start = highlightStartRelative,
                                                                            end = highlightEndRelative
                                                                        )
                                                                    }
                                                                }
                                                            } else {
                                                                searchHighlighted
                                                            }
                                                        TextWithEmphasis(
                                                            text = finalContent,
                                                            style = quoteStyle,
                                                            modifier = quoteModifier,
                                                            textMeasurer = textMeasurer,
                                                            onLinkClick = onLinkClickCallback,
                                                            onGeneralTap = onGeneralTapCallback,
                                                            block = block,
                                                            userHighlights = userHighlights,
                                                            activeSelection = activeSelection,
                                                            onSelectionChange = { sel ->
                                                                activeSelection = sel
                                                            },
                                                            onHighlightClick = { highlight, rect ->
                                                                activeHighlightForMenu =
                                                                    highlight to rect
                                                                activeSelection = null
                                                                menuState = null
                                                            },
                                                            isDarkTheme = isDarkTheme,
                                                            onRegisterLayout = { layout, coords ->
                                                                if (block.cfi != null) blockLayoutMap[block.cfi] =
                                                                    Triple(
                                                                        layout,
                                                                        coords,
                                                                        block.startCharOffsetInSource
                                                                    )
                                                            })
                                                    }

                                                    is ListItemBlock -> {
                                                        Row(
                                                            modifier = paddingModifier,
                                                            verticalAlignment = Alignment.Top
                                                        ) {
                                                            val markerAreaModifier =
                                                                Modifier
                                                                    .width(32.dp)
                                                                    .padding(end = 8.dp)

                                                            if (block.itemMarkerImage != null) {
                                                                val imageRequest =
                                                                    Builder(LocalContext.current).data(
                                                                        File(
                                                                            block.itemMarkerImage
                                                                        )
                                                                    ).crossfade(true).build()
                                                                val imageSize = with(density) {
                                                                    (textStyle.fontSize.value * 0.8f).sp.toDp()
                                                                }

                                                                AsyncImage(
                                                                    model = imageRequest,
                                                                    contentDescription = "List item marker",
                                                                    modifier = markerAreaModifier.height(
                                                                        imageSize
                                                                    ),
                                                                    alignment = Alignment.CenterEnd,
                                                                    contentScale = ContentScale.FillHeight
                                                                )
                                                            } else if (block.itemMarker != null) {
                                                                Text(
                                                                    text = block.itemMarker,
                                                                    style = textStyle.copy(
                                                                        textAlign = TextAlign.End
                                                                    ),
                                                                    modifier = markerAreaModifier
                                                                )
                                                            }
                                                            val searchHighlighted =
                                                                highlightQueryInText(
                                                                    block.content,
                                                                    searchQuery,
                                                                    searchHighlightColor
                                                                )
                                                            val finalContent =
                                                                if (ttsHighlightInfo != null && block.cfi == ttsHighlightInfo.cfi) {
                                                                    buildAnnotatedString {
                                                                        append(searchHighlighted)

                                                                        val blockStartAbs =
                                                                            block.startCharOffsetInSource
                                                                        val blockEndAbs =
                                                                            block.startCharOffsetInSource + searchHighlighted.length
                                                                        val highlightStartAbs =
                                                                            ttsHighlightInfo.offset
                                                                        val highlightEndAbs =
                                                                            ttsHighlightInfo.offset + ttsHighlightInfo.text.length

                                                                        val intersectionStartAbs =
                                                                            maxOf(
                                                                                blockStartAbs,
                                                                                highlightStartAbs
                                                                            )
                                                                        val intersectionEndAbs = minOf(
                                                                            blockEndAbs, highlightEndAbs
                                                                        )

                                                                        if (intersectionStartAbs < intersectionEndAbs) {
                                                                            val highlightStartRelative =
                                                                                intersectionStartAbs - blockStartAbs
                                                                            val highlightEndRelative =
                                                                                intersectionEndAbs - blockStartAbs
                                                                            addStyle(
                                                                                style = SpanStyle(
                                                                                    background = ttsHighlightColor
                                                                                ),
                                                                                start = highlightStartRelative,
                                                                                end = highlightEndRelative
                                                                            )
                                                                        }
                                                                    }
                                                                } else {
                                                                    searchHighlighted
                                                                }
                                                            TextWithEmphasis(
                                                                text = finalContent,
                                                                style = textStyle,
                                                                modifier = Modifier.weight(1f),
                                                                textMeasurer = textMeasurer,
                                                                onLinkClick = onLinkClickCallback,
                                                                onGeneralTap = onGeneralTapCallback,
                                                                block = block,
                                                                userHighlights = userHighlights,
                                                                activeSelection = activeSelection,
                                                                onSelectionChange = { sel ->
                                                                    activeSelection = sel
                                                                },
                                                                onHighlightClick = { highlight, rect ->
                                                                    activeHighlightForMenu =
                                                                        highlight to rect
                                                                    activeSelection = null
                                                                    menuState = null
                                                                },
                                                                isDarkTheme = isDarkTheme,
                                                                onRegisterLayout = { layout, coords ->
                                                                    if (block.cfi != null) blockLayoutMap[block.cfi] =
                                                                        Triple(
                                                                            layout,
                                                                            coords,
                                                                            block.startCharOffsetInSource
                                                                        )
                                                                })
                                                        }
                                                    }

                                                    is WrappingContentBlock -> {
                                                        WrappingContentLayout(
                                                            block = block,
                                                            textStyle = textStyle,
                                                            modifier = paddingModifier,
                                                            searchQuery = searchQuery,
                                                            ttsHighlightInfo = ttsHighlightInfo,
                                                            searchHighlightColor = searchHighlightColor,
                                                            ttsHighlightColor = ttsHighlightColor
                                                        )
                                                    }

                                                    is FlexContainerBlock -> {
                                                        // Background, border, and padding are
                                                        // already applied by the outer Box wrapper.
                                                        // Only apply padding + width here.
                                                        val containerModifier = paddingModifier

                                                        if (block.style.flexDirection == "row") {
                                                            val horizontalArrangement =
                                                                when (block.style.justifyContent) {
                                                                    "center" -> Arrangement.Center
                                                                    "flex-end" -> Arrangement.End
                                                                    "space-between" -> Arrangement.SpaceBetween
                                                                    "space-around" -> Arrangement.SpaceAround
                                                                    else -> Arrangement.Start
                                                                }
                                                            val verticalAlignment =
                                                                when (block.style.alignItems) {
                                                                    "center" -> Alignment.CenterVertically
                                                                    "flex-end" -> Alignment.Bottom
                                                                    else -> Alignment.Top
                                                                }
                                                            Row(
                                                                modifier = containerModifier.fillMaxWidth(),
                                                                horizontalArrangement = horizontalArrangement,
                                                                verticalAlignment = verticalAlignment
                                                            ) {
                                                                block.children.forEach { childBlock ->
                                                                    RenderFlexChildBlock(
                                                                        childBlock = childBlock,
                                                                        textStyle = textStyle,
                                                                        searchQuery = searchQuery,
                                                                        searchHighlightColor = searchHighlightColor,
                                                                        ttsHighlightInfo = ttsHighlightInfo,
                                                                        ttsHighlightColor = ttsHighlightColor,
                                                                        textMeasurer = textMeasurer,
                                                                        onLinkClickCallback = onLinkClickCallback,
                                                                        onGeneralTapCallback = onGeneralTapCallback,
                                                                        userHighlights = userHighlights,
                                                                        activeSelection = activeSelection,
                                                                        onSelectionChange = { sel ->
                                                                            activeSelection = sel
                                                                        },
                                                                        onHighlightClick = { highlight, rect ->
                                                                            activeHighlightForMenu =
                                                                                highlight to rect
                                                                            activeSelection = null
                                                                            menuState = null
                                                                        },
                                                                        isDarkTheme = isDarkTheme,
                                                                        blockLayoutMap = blockLayoutMap,
                                                                        density = density,
                                                                        imageLoader = imageLoader
                                                                    )
                                                                }
                                                            }
                                                        } else {
                                                            val verticalArrangement =
                                                                when (block.style.justifyContent) {
                                                                    "center" -> Arrangement.Center
                                                                    "flex-end" -> Arrangement.Bottom
                                                                    "space-between" -> Arrangement.SpaceBetween
                                                                    "space-around" -> Arrangement.SpaceAround
                                                                    else -> Arrangement.Top
                                                                }
                                                            val horizontalAlignment =
                                                                when (block.style.alignItems) {
                                                                    "center" -> Alignment.CenterHorizontally
                                                                    "flex-end" -> Alignment.End
                                                                    else -> Alignment.Start
                                                                }
                                                            Column(
                                                                modifier = containerModifier.fillMaxWidth(),
                                                                verticalArrangement = verticalArrangement,
                                                                horizontalAlignment = horizontalAlignment
                                                            ) {
                                                                block.children.forEach { childBlock ->
                                                                    RenderFlexChildBlock(
                                                                        childBlock = childBlock,
                                                                        textStyle = textStyle,
                                                                        searchQuery = searchQuery,
                                                                        searchHighlightColor = searchHighlightColor,
                                                                        ttsHighlightInfo = ttsHighlightInfo,
                                                                        ttsHighlightColor = ttsHighlightColor,
                                                                        textMeasurer = textMeasurer,
                                                                        onLinkClickCallback = onLinkClickCallback,
                                                                        onGeneralTapCallback = onGeneralTapCallback,
                                                                        userHighlights = userHighlights,
                                                                        activeSelection = activeSelection,
                                                                        onSelectionChange = { sel ->
                                                                            activeSelection = sel
                                                                        },
                                                                        onHighlightClick = { highlight, rect ->
                                                                            activeHighlightForMenu =
                                                                                highlight to rect
                                                                            activeSelection = null
                                                                            menuState = null
                                                                        },
                                                                        isDarkTheme = isDarkTheme,
                                                                        blockLayoutMap = blockLayoutMap,
                                                                        density = density,
                                                                        imageLoader = imageLoader
                                                                    )
                                                                }
                                                            }
                                                        }
                                                    }

                                                    is MathBlock -> {
                                                        Timber.d(
                                                            "PaginatedReader: Rendering MathBlock. Alt: '${block.altText}', Has SVG: ${!block.svgContent.isNullOrBlank()}"
                                                        )
                                                        if (!block.svgContent.isNullOrBlank()) {
                                                            BoxWithConstraints(
                                                                modifier = paddingModifier
                                                            ) {
                                                                val localDensity = LocalDensity.current
                                                                val fontSizePx = with(localDensity) {
                                                                    textStyle.fontSize.toPx()
                                                                }
                                                                val containerWidthPx =
                                                                    with(localDensity) {
                                                                        maxWidth.roundToPx()
                                                                    }
                                                                val widthPx = parseSvgDimension(
                                                                    block.svgWidth,
                                                                    fontSizePx,
                                                                    containerWidthPx,
                                                                    localDensity
                                                                )
                                                                val heightPx = parseSvgDimension(
                                                                    block.svgHeight,
                                                                    fontSizePx,
                                                                    containerWidthPx,
                                                                    localDensity
                                                                )

                                                                var imageModifier: Modifier = Modifier
                                                                if (widthPx != null) {
                                                                    val finalWidthDp = with(localDensity) { widthPx.toDp() }
                                                                    Timber.d("Applying calculated width to MathBlock image: $finalWidthDp")
                                                                    imageModifier = imageModifier.width(finalWidthDp)
                                                                } else {
                                                                    Timber.w("Could not calculate a specific width for MathBlock. It will fill available space.")
                                                                    imageModifier = imageModifier.fillMaxWidth()
                                                                }

                                                                if (heightPx != null) {
                                                                    val finalHeightDp = with(localDensity) { heightPx.toDp() }
                                                                    Timber.d("Applying calculated height to MathBlock image: $finalHeightDp")
                                                                    imageModifier = imageModifier.height(finalHeightDp)
                                                                } else {
                                                                    val viewBoxParts = block.svgViewBox?.split(' ', ',')?.mapNotNull { it.toFloatOrNull() }
                                                                    if (viewBoxParts != null && viewBoxParts.size == 4 && viewBoxParts[2] > 0) {
                                                                        val aspectRatio = viewBoxParts[3] / viewBoxParts[2]
                                                                        val effectiveWidth = widthPx ?: containerWidthPx.toFloat()
                                                                        val finalHeightDp = with(localDensity) { (effectiveWidth * aspectRatio).toDp() }
                                                                        imageModifier = imageModifier.height(finalHeightDp)
                                                                    } else {
                                                                        val fallbackHeightDp = with(localDensity) { (textStyle.fontSize.value * 3).sp.toDp() }
                                                                        imageModifier = imageModifier.height(fallbackHeightDp)
                                                                    }
                                                                }

                                                                val imageRequest =
                                                                    Builder(LocalContext.current).data(
                                                                        SvgData(
                                                                            block.svgContent
                                                                        )
                                                                    ).listener(
                                                                        onError = { _, result ->
                                                                            Timber.e(
                                                                                result.throwable,
                                                                                "Coil failed to load SVG for MathBlock."
                                                                            )
                                                                        }).build()

                                                                val colorFilter =
                                                                    if (block.isFromMathJax) ColorFilter.tint(
                                                                        textStyle.color
                                                                    )
                                                                    else null

                                                                AsyncImage(
                                                                    model = imageRequest,
                                                                    contentDescription = block.altText
                                                                        ?: "Equation",
                                                                    modifier = imageModifier,
                                                                    contentScale = ContentScale.Fit,
                                                                    colorFilter = colorFilter,
                                                                    imageLoader = imageLoader
                                                                )
                                                            }
                                                        } else {
                                                            Timber.w(
                                                                "PaginatedReader: MathBlock has no SVG content, rendering alt text."
                                                            )
                                                            Text(
                                                                text = block.altText
                                                                    ?: "[Equation not available]",
                                                                style = textStyle,
                                                                modifier = paddingModifier
                                                            )
                                                        }
                                                    }

                                                    is ImageBlock -> {
                                                        val style = block.style
                                                        val finalImageModifier = Modifier
                                                            .then(
                                                                if (style.width != Dp.Unspecified) Modifier.width(
                                                                    style.width
                                                                )
                                                                else Modifier
                                                            )
                                                            .then(
                                                                if (style.maxWidth != Dp.Unspecified) Modifier.widthIn(
                                                                    max = style.maxWidth
                                                                )
                                                                else Modifier
                                                            )
                                                            .then(
                                                                if (block.intrinsicWidth != null && block.intrinsicHeight != null && block.intrinsicWidth > 0f && block.intrinsicHeight > 0f) {
                                                                    Modifier.aspectRatio(block.intrinsicWidth / block.intrinsicHeight, matchHeightConstraintsFirst = false)
                                                                } else if (style.height != Dp.Unspecified) {
                                                                    Modifier.height(style.height)
                                                                } else {
                                                                    Modifier.height(250.dp)
                                                                }
                                                            )
                                                            .then(paddingModifier)

                                                        val colorFilter =
                                                            if (block.style.filter == "invert(100%)") {
                                                                val matrix = floatArrayOf(
                                                                    -1f,
                                                                    0f,
                                                                    0f,
                                                                    0f,
                                                                    255f,
                                                                    0f,
                                                                    -1f,
                                                                    0f,
                                                                    0f,
                                                                    255f,
                                                                    0f,
                                                                    0f,
                                                                    -1f,
                                                                    0f,
                                                                    255f,
                                                                    0f,
                                                                    0f,
                                                                    0f,
                                                                    1f,
                                                                    0f
                                                                )
                                                                ColorFilter.colorMatrix(
                                                                    ColorMatrix(matrix)
                                                                )
                                                            } else {
                                                                null
                                                            }
                                                        val context = LocalContext.current
                                                        val imageRequest =
                                                            Builder(context).data(File(block.path))
                                                                .listener(onSuccess = { _, _ ->
                                                                    Timber.d(
                                                                        "Coil successfully loaded image: ${block.path}"
                                                                    )
                                                                }, onError = { _, result ->
                                                                    Timber.e(
                                                                        result.throwable,
                                                                        "Coil FAILED to load image: ${block.path}"
                                                                    )
                                                                }).crossfade(true).build()

                                                        AsyncImage(
                                                            model = imageRequest,
                                                            contentDescription = block.altText
                                                                ?: "Image from EPUB",
                                                            modifier = finalImageModifier,
                                                            contentScale = ContentScale.Fit,
                                                            colorFilter = colorFilter
                                                        )
                                                    }

                                                    is SpacerBlock -> {
                                                        Box(
                                                            modifier = Modifier
                                                                .fillMaxWidth()
                                                                .height(block.height)
                                                                .drawCssBorders(block.style, density)
                                                        )
                                                    }

                                                    is TableBlock -> {
                                                        Column(modifier = paddingModifier) {
                                                            block.rows.forEach { tableRow ->
                                                                Row(
                                                                    Modifier
                                                                        .fillMaxWidth()
                                                                        .height(
                                                                            IntrinsicSize.Min
                                                                        )
                                                                ) {
                                                                    val hasFixedWidths = tableRow.any {
                                                                        it.style.blockStyle.width != Dp.Unspecified
                                                                    }

                                                                    tableRow.forEach { cell ->
                                                                        val cellStyle =
                                                                            cell.style.blockStyle

                                                                        val cellContainerModifier =
                                                                            if (hasFixedWidths) {
                                                                                if (cellStyle.width != Dp.Unspecified) Modifier.width(
                                                                                    cellStyle.width
                                                                                )
                                                                                else Modifier.weight(
                                                                                    cell.colspan.toFloat(),
                                                                                    fill = true
                                                                                )
                                                                            } else {
                                                                                Modifier.weight(
                                                                                    cell.colspan.toFloat(),
                                                                                    fill = true
                                                                                )
                                                                            }

                                                                        val alignment =
                                                                            when (cell.style.paragraphStyle.textAlign) {
                                                                                TextAlign.Center -> Alignment.CenterHorizontally
                                                                                TextAlign.End -> Alignment.End
                                                                                else -> Alignment.Start
                                                                            }

                                                                        val cellModifier =
                                                                            cellContainerModifier
                                                                                .fillMaxHeight()
                                                                                .then(
                                                                                    if (cellStyle.backgroundColor.isSpecified) {
                                                                                        Modifier.background(
                                                                                            cellStyle.backgroundColor
                                                                                        )
                                                                                    } else {
                                                                                        Modifier
                                                                                    }
                                                                                )
                                                                                .drawCssBorders(cellStyle, density)
                                                                                .padding(
                                                                                    start = cellStyle.padding.left.coerceAtLeast(
                                                                                        0.dp
                                                                                    ),
                                                                                    top = cellStyle.padding.top.coerceAtLeast(
                                                                                        0.dp
                                                                                    ),
                                                                                    end = cellStyle.padding.right.coerceAtLeast(
                                                                                        0.dp
                                                                                    ),
                                                                                    bottom = cellStyle.padding.bottom.coerceAtLeast(
                                                                                        0.dp
                                                                                    )
                                                                                )

                                                                        Column(
                                                                            modifier = cellModifier,
                                                                            horizontalAlignment = alignment
                                                                        ) {
                                                                            val cellTextStyle =
                                                                                if (cell.isHeader) {
                                                                                    textStyle.copy(
                                                                                        fontWeight = FontWeight.Bold
                                                                                    )
                                                                                } else {
                                                                                    textStyle
                                                                                }

                                                                            cell.content.forEach { blockInCell ->
                                                                                when (blockInCell) {
                                                                                    is ParagraphBlock -> {
                                                                                        Text(
                                                                                            text = blockInCell.content,
                                                                                            style = cellTextStyle,
                                                                                            modifier = Modifier.fillMaxWidth()
                                                                                        )
                                                                                    }

                                                                                    is HeaderBlock -> {
                                                                                        Text(
                                                                                            text = blockInCell.content,
                                                                                            style = cellTextStyle.copy(
                                                                                                fontWeight = FontWeight.Bold
                                                                                            ),
                                                                                            modifier = Modifier.fillMaxWidth()
                                                                                        )
                                                                                    }

                                                                                    is ListItemBlock -> {
                                                                                        Row(
                                                                                            verticalAlignment = Alignment.Top
                                                                                        ) {
                                                                                            if (blockInCell.itemMarker != null) {
                                                                                                Text(
                                                                                                    text = blockInCell.itemMarker,
                                                                                                    style = cellTextStyle,
                                                                                                    modifier = Modifier.padding(
                                                                                                        end = 4.dp
                                                                                                    )
                                                                                                )
                                                                                            }
                                                                                            Text(
                                                                                                text = blockInCell.content,
                                                                                                style = cellTextStyle,
                                                                                                modifier = Modifier.weight(
                                                                                                    1f
                                                                                                )
                                                                                            )
                                                                                        }
                                                                                    }

                                                                                    is SpacerBlock -> {
                                                                                        Spacer(
                                                                                            modifier = Modifier
                                                                                                .fillMaxWidth()
                                                                                                .height(blockInCell.height)
                                                                                                .drawCssBorders(blockInCell.style, density)
                                                                                        )
                                                                                    }

                                                                                    is ImageBlock -> {
                                                                                        val imageModifier = Modifier.fillMaxWidth().then(
                                                                                            if (blockInCell.intrinsicWidth != null && blockInCell.intrinsicHeight != null && blockInCell.intrinsicWidth > 0f && blockInCell.intrinsicHeight > 0f) {
                                                                                                Modifier.aspectRatio(blockInCell.intrinsicWidth / blockInCell.intrinsicHeight, matchHeightConstraintsFirst = false)
                                                                                            } else if (blockInCell.style.height != Dp.Unspecified) {
                                                                                                Modifier.height(blockInCell.style.height)
                                                                                            } else {
                                                                                                Modifier.height(250.dp)
                                                                                            }
                                                                                        )
                                                                                        AsyncImage(
                                                                                            model = Builder(
                                                                                                LocalContext.current
                                                                                            ).data(
                                                                                                File(
                                                                                                    blockInCell.path
                                                                                                )
                                                                                            ).build(),
                                                                                            contentDescription = blockInCell.altText,
                                                                                            contentScale = ContentScale.Fit,
                                                                                            modifier = imageModifier
                                                                                        )
                                                                                    }
                                                                                    is TextContentBlock -> {
                                                                                        Text(
                                                                                            text = blockInCell.content,
                                                                                            style = cellTextStyle,
                                                                                            modifier = Modifier.fillMaxWidth()
                                                                                        )
                                                                                    }

                                                                                    else -> {}
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    var chapterInfo by remember {
                                        mutableStateOf<Pair<String, Int?>?>(null)
                                    }
                                    LaunchedEffect(pageIndex) {
                                        chapterInfo = onGetChapterInfo(pageIndex)
                                    }

                                    ChapterLoadingPlaceholder(title = chapterInfo?.first)
                                }
                            }
                        }
                    }
                }
            }
            menuState?.let { state ->
                Popup(popupPositionProvider = remember(state.rect, density) {
                    SmartPopupPositionProvider(state.rect, density)
                }, onDismissRequest = { state.onHide() }) {
                    PaginatedTextSelectionMenu(
                        onCopy = {
                            isForDictionary = false
                            isForHighlight = false
                            state.onCopy()
                            state.onHide()
                        }, onSelectAll = {
                            state.onSelectAll?.invoke()
                            state.onHide()
                        }, onTts = {
                            isForHighlight = true
                            state.onCopy()
                            isForHighlight = false

                            capturedTextForAction?.let { text ->
                                val selectionRect = state.rect
                                var geometricSuccess = false
                                val candidates = blockLayoutMap.filter { (_, triple) ->
                                    val (_, coords, _) = triple
                                    if (!coords.isAttached) return@filter false
                                    val pos = coords.positionInWindow()
                                    val size = coords.size.toSize()
                                    Rect(pos, size).overlaps(selectionRect)
                                }
                                if (candidates.isNotEmpty()) {
                                    try {
                                        val sorted = candidates.entries.sortedBy { it.value.second.positionInWindow().y }
                                        val firstEntry = sorted.first()
                                        val startCfi: String = firstEntry.key
                                        val startTriple = firstEntry.value

                                        val startLayout: TextLayoutResult = startTriple.first
                                        val startCoords: LayoutCoordinates = startTriple.second
                                        val startAbsOffset: Int = startTriple.third

                                        val localStart = startCoords.windowToLocal(selectionRect.topLeft)
                                        val finalStartOffset = startLayout.getOffsetForPosition(localStart)
                                        val absStart: Int = finalStartOffset + startAbsOffset

                                        onStartTtsFromSelection(startCfi, absStart)
                                        geometricSuccess = true
                                    } catch(e: Exception) {
                                        Timber.e(e, "TTS Selection error")
                                    }
                                }
                                if (!geometricSuccess) {
                                    val pageInfo = onGetPage(pagerState.currentPage)
                                    val firstCfi = pageInfo?.content?.firstOrNull { it.cfi != null }?.cfi
                                    if (firstCfi != null) {
                                        onStartTtsFromSelection(firstCfi, 0)
                                    }
                                }
                            }
                            state.onHide()
                        }, onDictionary = {
                            isForDictionary = true
                            state.onCopy()
                            isForDictionary = false
                            state.onHide()
                        }, onTranslate = {
                            state.onCopy()
                            onTranslate(capturedTextForAction ?: "")
                            state.onHide()
                        }, onSearch = {
                            onSearch(capturedTextForAction ?: "")
                            state.onHide()
                        }, onHighlight = { color ->
                            Timber.d("Menu: Highlight option clicked. Color: ${color.id}")
                            isForHighlight = true
                            state.onCopy()
                            isForHighlight = false

                            capturedTextForAction?.let { text ->
                                val selectionRect = state.rect
                                Timber.d("Menu: Selection Rect: $selectionRect")

                                var geometricSuccess = false
                                val candidates = blockLayoutMap.filter { (_, triple) ->
                                    val (_, coords, _) = triple
                                    if (!coords.isAttached) return@filter false
                                    val pos = coords.positionInWindow()
                                    val size = coords.size.toSize()
                                    val blockRect = Rect(pos, size)
                                    val overlaps = blockRect.overlaps(selectionRect)
                                    overlaps
                                }

                                if (candidates.isNotEmpty()) {
                                    Timber.d(
                                        "Menu: Geometric candidates found: ${candidates.keys}"
                                    )
                                    try {
                                        val sorted = candidates.entries.sortedBy {
                                            it.value.second.positionInWindow().y
                                        }
                                        val (startCfi, startTriple) = sorted.first()
                                        val (endCfi, endTriple) = sorted.last()
                                        val (startLayout, startCoords, startAbsOffset) = startTriple
                                        val (endLayout, endCoords, endAbsOffset) = endTriple
                                        val localStart =
                                            startCoords.windowToLocal(selectionRect.topLeft)
                                        val localEnd = endCoords.windowToLocal(
                                            selectionRect.bottomRight
                                        )
                                        var finalStartOffset =
                                            startLayout.getOffsetForPosition(localStart)
                                        var finalEndOffset = endLayout.getOffsetForPosition(localEnd)
                                        var finalEndCfi = endCfi
                                        val startText = startLayout.layoutInput.text.text

                                        if (startCfi == endCfi) {
                                            val matches = mutableListOf<Int>()
                                            var idx = startText.indexOf(text)
                                            while (idx != -1) {
                                                matches.add(idx)
                                                idx = startText.indexOf(text, idx + 1)
                                            }
                                            if (matches.isNotEmpty()) {
                                                val bestMatch = matches.minBy {
                                                    abs(it - finalStartOffset)
                                                }
                                                finalStartOffset = bestMatch
                                                finalEndOffset = bestMatch + text.length
                                                Timber.d(
                                                    "Refined Single-Block Offset: $finalStartOffset"
                                                )
                                            }
                                        } else {
                                            val endText = endLayout.layoutInput.text.text

                                            fun findBestMatch(
                                                source: String,
                                                query: String,
                                                targetOffset: Int,
                                                isSuffix: Boolean
                                            ): Int {
                                                if (query.isEmpty()) return -1
                                                var bestIdx = -1
                                                var minDiff = Int.MAX_VALUE
                                                var idx = source.indexOf(query)
                                                while (idx != -1) {
                                                    val cmpPoint = if (isSuffix) idx + query.length
                                                    else idx
                                                    val diff = abs(cmpPoint - targetOffset)
                                                    if (diff < minDiff) {
                                                        minDiff = diff
                                                        bestIdx = idx
                                                    }
                                                    idx = source.indexOf(query, idx + 1)
                                                }
                                                return bestIdx
                                            }

                                            var sMatch = -1
                                            var eMatch = -1
                                            var usedSuffixLen = 0

                                            val maxChunk = minOf(text.length, 50)
                                            for (len in maxChunk downTo 3) {
                                                val prefix = text.take(len).trim()
                                                if (prefix.isNotEmpty()) {
                                                    val idx = findBestMatch(
                                                        startText,
                                                        prefix,
                                                        finalStartOffset,
                                                        isSuffix = false
                                                    )
                                                    if (idx != -1) {
                                                        sMatch = idx
                                                        Timber.d(
                                                            "Refined Start: Found prefix '$prefix' at $idx"
                                                        )
                                                        break
                                                    }
                                                }
                                            }

                                            for (len in maxChunk downTo 3) {
                                                val suffix = text.takeLast(len).trim()
                                                if (suffix.isNotEmpty()) {
                                                    val idx = findBestMatch(
                                                        endText,
                                                        suffix,
                                                        finalEndOffset,
                                                        isSuffix = true
                                                    )
                                                    if (idx != -1) {
                                                        eMatch = idx
                                                        usedSuffixLen = suffix.length
                                                        Timber.d(
                                                            "Refined End: Found suffix '$suffix' at $idx"
                                                        )
                                                        break
                                                    }
                                                }
                                            }

                                            if (sMatch != -1 && eMatch != -1) {
                                                finalStartOffset = sMatch
                                                finalEndOffset = eMatch + usedSuffixLen
                                            } else if (eMatch == -1) {
                                                Timber.d(
                                                    "Refined: Suffix not found in end block. Checking single block fit."
                                                )
                                                val fitIdx = startText.indexOf(text)
                                                if (fitIdx != -1) {
                                                    finalEndCfi = startCfi
                                                    finalStartOffset = fitIdx
                                                    finalEndOffset = fitIdx + text.length
                                                }
                                            }
                                        }

                                        val absStart = finalStartOffset + startAbsOffset
                                        val absEnd =
                                            finalEndOffset + if (startCfi == finalEndCfi) startAbsOffset
                                            else endAbsOffset

                                        val rangeCfi = if (startCfi == finalEndCfi) {
                                            val actualStart = minOf(absStart, absEnd)
                                            val actualEnd = maxOf(absStart, absEnd).coerceAtLeast(
                                                actualStart + 1
                                            )
                                            "$startCfi:$actualStart|$finalEndCfi:$actualEnd"
                                        } else {
                                            "$startCfi:$absStart|$finalEndCfi:$absEnd"
                                        }

                                        Timber.d("Menu: Geometric Success. CFI: $rangeCfi")
                                        onHighlightCreated(rangeCfi, text, color.id)
                                        geometricSuccess = true
                                    } catch (e: Exception) {
                                        Timber.e(e, "Menu: Geometric calculation failed.")
                                    }
                                }

                                if (!geometricSuccess) {
                                    Timber.w("Menu: Falling back to text search.")
                                    val pageContent = onGetPage(pagerState.currentPage)
                                    val textBlocks =
                                        pageContent?.content?.filterIsInstance<TextContentBlock>()
                                            ?.filter { it.cfi != null } ?: emptyList()

                                    var startBlock: TextContentBlock? = null
                                    var endBlock: TextContentBlock? = null
                                    var startOffsetRel = -1
                                    var endOffsetRel = -1

                                    for (block in textBlocks) {
                                        val content = block.content.text
                                        val idx = content.indexOf(text)
                                        if (idx != -1) {
                                            startBlock = block
                                            endBlock = block
                                            startOffsetRel = idx
                                            endOffsetRel = idx + text.length
                                            break
                                        }
                                    }

                                    endBlock = startBlock

                                    if (startBlock != null) {
                                        val startAbs =
                                            startBlock.startCharOffsetInSource + startOffsetRel
                                        val endAbs =
                                            endBlock.startCharOffsetInSource + (if (endOffsetRel != -1) endOffsetRel
                                            else startOffsetRel + text.length)
                                        onHighlightCreated(
                                            "${startBlock.cfi}:$startAbs|${endBlock.cfi}:$endAbs",
                                            text,
                                            color.id
                                        )
                                    }
                                }
                            }
                            state.onHide()
                        }, onDelete = null, isProUser = isProUser, isOss = isOss,
                        activeHighlightPalette = activeHighlightPalette,
                        onOpenPaletteManager = { showPaletteManager = true }
                    )
                }
            }

            if (activeSelection != null) {
                val sel = activeSelection!!
                Popup(popupPositionProvider = remember(sel.rect, density) {
                    SmartPopupPositionProvider(sel.rect, density)
                }, onDismissRequest = { activeSelection = null }) {
                    PaginatedTextSelectionMenu(
                        onCopy = {
                            val clipboardManager =
                                context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
                            val clip = ClipData.newPlainText("Copied Text", sel.text)
                            clipboardManager.setPrimaryClip(clip)
                            activeSelection = null
                        }, onSelectAll = null, onDictionary = {
                            if (isProUser || countWords(sel.text) <= 1) {
                                onWordSelectedForAiDefinition(sel.text)
                            } else {
                                onShowDictionaryUpsellDialog()
                            }
                            activeSelection = null
                        }, onTranslate = {
                            onTranslate(sel.text)
                            activeSelection = null
                        }, onSearch = {
                            onSearch(sel.text)
                            activeSelection = null
                        }, onTts = {
                            onStartTtsFromSelection(sel.baseCfi, sel.startOffset)
                            activeSelection = null
                        }, onHighlight = { color ->
                            Timber.d(
                                "CustomSelection: Highlight clicked. Text: '${sel.text}', BaseCFI: ${sel.baseCfi}, StartOffset: ${sel.startOffset}"
                            )
                            val finalCfi = if (sel.startOffset > 0) "${sel.baseCfi}:${sel.startOffset}"
                            else sel.baseCfi
                            onHighlightCreated(finalCfi, sel.text, color.id)
                            activeSelection = null
                        }, onDelete = null, isProUser = isProUser, isOss = isOss,
                        activeHighlightPalette = activeHighlightPalette,
                        onOpenPaletteManager = { showPaletteManager = true }
                    )
                }
            }

            if (showColorPickerDialog != null) {
                AlertDialog(
                    onDismissRequest = { showColorPickerDialog = null },
                    title = { Text("Select Color") },
                    text = {
                        LazyVerticalGrid(
                            columns = GridCells.Adaptive(minSize = 48.dp),
                            horizontalArrangement = Arrangement.spacedBy(16.dp),
                            verticalArrangement = Arrangement.spacedBy(16.dp)
                        ) {
                            items(HighlightColor.entries) { colorOption ->
                                Box(
                                    modifier = Modifier
                                        .size(48.dp)
                                        .background(colorOption.color, CircleShape)
                                        .border(1.dp, MaterialTheme.colorScheme.outline, CircleShape)
                                        .clickable {
                                            onUpdatePalette(showColorPickerDialog!!, colorOption)
                                            showColorPickerDialog = null
                                        }
                                )
                            }
                        }
                    },
                    confirmButton = { TextButton(onClick = { showColorPickerDialog = null }) { Text("Close") } }
                )
            }

            // Edit Menu (Delete)
            if (activeHighlightForMenu != null) {
                val (highlight, rect) = activeHighlightForMenu!!
                Popup(popupPositionProvider = remember(rect, density) {
                    SmartPopupPositionProvider(rect, density)
                }, onDismissRequest = { activeHighlightForMenu = null }) {
                    PaginatedTextSelectionMenu(
                        onCopy = {
                            val clipboardManager =
                                context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
                            val clip = ClipData.newPlainText("Copied Text", highlight.text)
                            clipboardManager.setPrimaryClip(clip)
                            activeHighlightForMenu = null
                        },
                        onSelectAll = null,
                        onDictionary = {
                            if (isProUser || countWords(highlight.text) <= 1) {
                                onWordSelectedForAiDefinition(highlight.text)
                            } else {
                                onShowDictionaryUpsellDialog()
                            }
                            activeHighlightForMenu = null
                        },
                        onTranslate = {
                            onTranslate(highlight.text)
                            activeHighlightForMenu = null
                        },
                        onSearch = {
                            onSearch(highlight.text)
                            activeHighlightForMenu = null
                        },
                        onTts = {
                            val firstPart = highlight.cfi.split("|").first()
                            val baseCfi = firstPart.substringBefore(":")
                            val offset = firstPart.substringAfter(":", "0").toIntOrNull() ?: 0
                            onStartTtsFromSelection(baseCfi, offset)
                            activeHighlightForMenu = null
                        },
                        onHighlight = { color ->
                            Timber.d("Menu: Updating highlight color to ${color.id}")
                            onHighlightDeleted(highlight.cfi)
                            onHighlightCreated(highlight.cfi, highlight.text, color.id)
                            activeHighlightForMenu = null
                        },
                        onDelete = {
                            onHighlightDeleted(highlight.cfi)
                            activeHighlightForMenu = null
                        },
                        isProUser = isProUser,
                        isOss = isOss,
                        activeHighlightPalette = activeHighlightPalette,
                        onOpenPaletteManager = { showPaletteManager = true }
                    )
                }
            }

            if (showPaletteManager) {
                PaletteManagerDialog(
                    currentPalette = activeHighlightPalette,
                    onDismiss = { showPaletteManager = false },
                    onSave = { newPalette ->
                        newPalette.forEachIndexed { index, color ->
                            onUpdatePalette(index, color)
                        }
                        showPaletteManager = false
                    }
                )
            }
        } else {
            Timber.w("Book has no pages to display.")
            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                Text("This book has no content to display.")
            }
        }
    }
}

@Composable
private fun ChapterLoadingPlaceholder(title: String?) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .padding(horizontal = 32.dp, vertical = 64.dp),
        contentAlignment = Alignment.Center
    ) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            if (title != null) {
                Text(
                    text = title,
                    style = MaterialTheme.typography.headlineSmall,
                    textAlign = TextAlign.Center,
                    color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f)
                )
                Spacer(Modifier.height(16.dp))
            }
            CircularProgressIndicator()
            Spacer(Modifier.height(16.dp))
            Text(
                text = "Preparing chapter.",
                style = MaterialTheme.typography.bodyMedium,
                textAlign = TextAlign.Center,
                color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
            )
        }
    }
}

private class MenuActionItem(
    val iconRes: Int? = null,
    val imageVector: androidx.compose.ui.graphics.vector.ImageVector? = null,
    val label: String,
    val onClick: () -> Unit,
    val isError: Boolean = false
)

@Composable
fun PaginatedTextSelectionMenu(
    onCopy: () -> Unit,
    onSelectAll: (() -> Unit)?,
    onDictionary: () -> Unit,
    onTranslate: () -> Unit,
    onSearch: () -> Unit,
    onHighlight: ((HighlightColor) -> Unit)?,
    onDelete: (() -> Unit)?,
    onTts: (() -> Unit)?,
    @Suppress("unused") isProUser: Boolean,
    @Suppress("unused") isOss: Boolean,
    activeHighlightPalette: List<HighlightColor> = emptyList(),
    onOpenPaletteManager: (() -> Unit)? = null
) {
    Surface(
        shape = RoundedCornerShape(12.dp),
        shadowElevation = 6.dp,
        color = MaterialTheme.colorScheme.surface,
        border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
    ) {
        Column(modifier = Modifier.width(IntrinsicSize.Max)) {
            // 1. Colors Row
            if (onHighlight != null) {
                Row(
                    modifier = Modifier
                        .padding(vertical = 12.dp, horizontal = 12.dp)
                        .fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    activeHighlightPalette.forEach { colorEnum ->
                        Box(
                            modifier = Modifier
                                .padding(horizontal = 6.dp)
                                .size(32.dp)
                                .background(colorEnum.color, CircleShape)
                                .clickable { onHighlight(colorEnum) }
                        )
                    }

                    if (onOpenPaletteManager != null) {
                        Spacer(modifier = Modifier.width(8.dp))
                        SpectrumButton(
                            onClick = onOpenPaletteManager,
                            size = 32.dp
                        )
                    }
                }
                HorizontalDivider()
            }

            val actions = mutableListOf<MenuActionItem>()
            actions.add(MenuActionItem(iconRes = R.drawable.copy, label = "Copy", onClick = onCopy))
            if (onTts != null) {
                actions.add(MenuActionItem(imageVector = Icons.AutoMirrored.Filled.VolumeUp, label = "Speak", onClick = onTts))
            }
            actions.add(MenuActionItem(iconRes = R.drawable.dictionary, label = "Dict", onClick = onDictionary))
            actions.add(MenuActionItem(iconRes = R.drawable.translate, label = "Translate", onClick = onTranslate))
            actions.add(MenuActionItem(iconRes = R.drawable.search, label = "Search", onClick = onSearch))

            if (onSelectAll != null) {
                actions.add(MenuActionItem(iconRes = R.drawable.select_all, label = "Select All", onClick = onSelectAll))
            }
            if (onDelete != null) {
                actions.add(MenuActionItem(imageVector = Icons.Default.Delete, label = "Remove", onClick = onDelete, isError = true))
            }

            Column(modifier = Modifier.padding(bottom = 4.dp)) {
                actions.chunked(3).forEach { rowActions ->
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 8.dp, vertical = 4.dp),
                        horizontalArrangement = Arrangement.SpaceEvenly,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        rowActions.forEach { action ->
                            val tint = if (action.isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurface
                            Column(
                                modifier = Modifier
                                    .width(64.dp)
                                    .clickable { action.onClick() }
                                    .padding(vertical = 8.dp),
                                horizontalAlignment = Alignment.CenterHorizontally
                            ) {
                                if (action.imageVector != null) {
                                    Icon(imageVector = action.imageVector, contentDescription = action.label, tint = tint, modifier = Modifier.size(24.dp))
                                } else if (action.iconRes != null) {
                                    Icon(painter = painterResource(id = action.iconRes), contentDescription = action.label, tint = tint, modifier = Modifier.size(24.dp))
                                }
                                Spacer(modifier = Modifier.height(4.dp))
                                Text(text = action.label, style = MaterialTheme.typography.labelSmall, color = tint, maxLines = 1)
                            }
                        }
                        repeat(3 - rowActions.size) {
                            Spacer(modifier = Modifier.width(64.dp))
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun RenderFlexChildBlock(
    childBlock: ContentBlock,
    textStyle: TextStyle,
    searchQuery: String,
    searchHighlightColor: Color,
    ttsHighlightInfo: TtsHighlightInfo?,
    ttsHighlightColor: Color,
    textMeasurer: TextMeasurer,
    onLinkClickCallback: (String) -> Unit,
    onGeneralTapCallback: (Offset) -> Unit,
    userHighlights: List<UserHighlight>,
    activeSelection: PaginatedSelection?,
    onSelectionChange: (PaginatedSelection?) -> Unit,
    onHighlightClick: (UserHighlight, Rect) -> Unit,
    isDarkTheme: Boolean,
    blockLayoutMap: MutableMap<String, Triple<TextLayoutResult, LayoutCoordinates, Int>>,
    density: Density,
    imageLoader: ImageLoader
) {
    @Composable
    fun renderTextBlock(block: TextContentBlock) {
        val searchHighlighted =
            highlightQueryInText(block.content, searchQuery, searchHighlightColor)
        val finalContent = if (ttsHighlightInfo != null && block.cfi == ttsHighlightInfo.cfi) {
            buildAnnotatedString {
                append(searchHighlighted)
                val blockStartAbs = block.startCharOffsetInSource
                val blockEndAbs = block.startCharOffsetInSource + searchHighlighted.length
                val highlightStartAbs = ttsHighlightInfo.offset
                val highlightEndAbs = ttsHighlightInfo.offset + ttsHighlightInfo.text.length

                val intersectionStartAbs = maxOf(blockStartAbs, highlightStartAbs)
                val intersectionEndAbs = minOf(blockEndAbs, highlightEndAbs)

                if (intersectionStartAbs < intersectionEndAbs) {
                    val highlightStartRelative = intersectionStartAbs - blockStartAbs
                    val highlightEndRelative = intersectionEndAbs - blockStartAbs
                    addStyle(
                        style = SpanStyle(background = ttsHighlightColor),
                        start = highlightStartRelative,
                        end = highlightEndRelative
                    )
                }
            }
        } else {
            searchHighlighted
        }

        // Apply block specific styles (like header font weight)
        val finalStyle = if (block is HeaderBlock) {
            textStyle.copy(
                fontWeight = FontWeight.Bold, fontSize = textStyle.fontSize * block.level.let {
                    when (it) {
                        1 -> 1.5f
                        2 -> 1.4f
                        3 -> 1.3f
                        4 -> 1.2f
                        5 -> 1.1f
                        else -> 1.0f
                    }
                })
        } else {
            textStyle
        }

        TextWithEmphasis(
            text = finalContent,
            style = finalStyle,
            modifier = Modifier,
            textMeasurer = textMeasurer,
            onLinkClick = onLinkClickCallback,
            onGeneralTap = onGeneralTapCallback,
            block = block,
            userHighlights = userHighlights,
            activeSelection = activeSelection,
            onSelectionChange = onSelectionChange,
            onHighlightClick = onHighlightClick,
            isDarkTheme = isDarkTheme,
            onRegisterLayout = { layout, coords ->
                block.cfi?.let { cfi ->
                    blockLayoutMap[cfi] = Triple(layout, coords, block.startCharOffsetInSource)
                }
            })
    }

    when (childBlock) {
        is ListItemBlock -> {
            Row(modifier = Modifier, verticalAlignment = Alignment.Top) {
                val markerAreaModifier = Modifier
                    .width(32.dp)
                    .padding(end = 8.dp)

                if (childBlock.itemMarkerImage != null) {
                    val imageRequest =
                        Builder(LocalContext.current).data(File(childBlock.itemMarkerImage))
                            .crossfade(true).build()
                    val imageSize = with(density) { (textStyle.fontSize.value * 0.8f).sp.toDp() }

                    AsyncImage(
                        model = imageRequest,
                        contentDescription = "List item marker",
                        modifier = markerAreaModifier.height(imageSize),
                        alignment = Alignment.CenterEnd,
                        contentScale = ContentScale.FillHeight
                    )
                } else if (childBlock.itemMarker != null) {
                    Text(
                        text = childBlock.itemMarker,
                        style = textStyle.copy(textAlign = TextAlign.End),
                        modifier = markerAreaModifier
                    )
                }

                // Reuse text rendering logic
                renderTextBlock(childBlock)
            }
        }

        is ParagraphBlock -> renderTextBlock(childBlock)
        is HeaderBlock -> renderTextBlock(childBlock)
        is QuoteBlock -> renderTextBlock(childBlock)
        is TextContentBlock -> renderTextBlock(childBlock)
        is ImageBlock -> {
            val style = childBlock.style
            val imageModifier = Modifier
                .then(
                    if (style.width != Dp.Unspecified) Modifier.width(style.width)
                    else Modifier
                )
                .then(
                    if (style.maxWidth != Dp.Unspecified) Modifier.widthIn(max = style.maxWidth)
                    else Modifier
                )
                .then(
                    if (childBlock.intrinsicWidth != null && childBlock.intrinsicHeight != null && childBlock.intrinsicWidth > 0f && childBlock.intrinsicHeight > 0f) {
                        Modifier.aspectRatio(childBlock.intrinsicWidth / childBlock.intrinsicHeight, matchHeightConstraintsFirst = false)
                    } else if (style.height != Dp.Unspecified) {
                        Modifier.height(style.height)
                    } else {
                        Modifier.height(250.dp)
                    }
                )

            val colorFilter = if (childBlock.style.filter == "invert(100%)") {
                val matrix = floatArrayOf(
                    -1f,
                    0f,
                    0f,
                    0f,
                    255f,
                    0f,
                    -1f,
                    0f,
                    0f,
                    255f,
                    0f,
                    0f,
                    -1f,
                    0f,
                    255f,
                    0f,
                    0f,
                    0f,
                    1f,
                    0f
                )
                ColorFilter.colorMatrix(ColorMatrix(matrix))
            } else null

            AsyncImage(
                model = Builder(LocalContext.current).data(File(childBlock.path)).crossfade(true)
                    .build(),
                contentDescription = childBlock.altText,
                modifier = imageModifier,
                contentScale = ContentScale.Fit,
                colorFilter = colorFilter,
                imageLoader = imageLoader
            )
        }

        is SpacerBlock -> {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(childBlock.height)
                    .drawCssBorders(childBlock.style, density)
            )
        }

        is TableBlock -> {
            Column(modifier = Modifier.fillMaxWidth()) {
                childBlock.rows.forEach { tableRow ->
                    Row(
                        Modifier
                            .fillMaxWidth()
                            .height(IntrinsicSize.Min)
                    ) {
                        val hasFixedWidths =
                            tableRow.any { it.style.blockStyle.width != Dp.Unspecified }

                        tableRow.forEach { cell ->
                            val cellStyle = cell.style.blockStyle
                            val cellModifier = Modifier
                                .fillMaxHeight()
                                .then(
                                    if (hasFixedWidths && cellStyle.width != Dp.Unspecified) Modifier.width(
                                        cellStyle.width
                                    )
                                    else Modifier.weight(
                                        cell.colspan.toFloat(), fill = true
                                    )
                                )
                                .then(
                                    if (cellStyle.backgroundColor.isSpecified) Modifier.background(
                                        cellStyle.backgroundColor
                                    )
                                    else Modifier
                                )
                                .drawCssBorders(cellStyle, density)
                                .padding(
                                    start = cellStyle.padding.left.coerceAtLeast(
                                        0.dp
                                    ),
                                    top = cellStyle.padding.top.coerceAtLeast(0.dp),
                                    end = cellStyle.padding.right.coerceAtLeast(
                                        0.dp
                                    ),
                                    bottom = cellStyle.padding.bottom.coerceAtLeast(
                                        0.dp
                                    )
                                )

                            val alignment = when (cell.style.paragraphStyle.textAlign) {
                                TextAlign.Center -> Alignment.CenterHorizontally
                                TextAlign.End -> Alignment.End
                                else -> Alignment.Start
                            }

                            Column(modifier = cellModifier, horizontalAlignment = alignment) {
                                val cellTextStyle =
                                    if (cell.isHeader) textStyle.copy(fontWeight = FontWeight.Bold)
                                    else textStyle
                                cell.content.forEach { blockInCell ->
                                    if (blockInCell is TextContentBlock) {
                                        Text(
                                            text = blockInCell.content,
                                            style = cellTextStyle,
                                            modifier = Modifier.fillMaxWidth()
                                        )
                                    } else if (blockInCell is ImageBlock) {
                                        val imageModifier = Modifier.fillMaxWidth().then(
                                            if (blockInCell.intrinsicWidth != null && blockInCell.intrinsicHeight != null && blockInCell.intrinsicWidth > 0f && blockInCell.intrinsicHeight > 0f) {
                                                Modifier.aspectRatio(blockInCell.intrinsicWidth / blockInCell.intrinsicHeight, matchHeightConstraintsFirst = false)
                                            } else if (blockInCell.style.height != Dp.Unspecified) {
                                                Modifier.height(blockInCell.style.height)
                                            } else {
                                                Modifier.height(250.dp)
                                            }
                                        )
                                        AsyncImage(
                                            model = Builder(LocalContext.current).data(
                                                File(
                                                    blockInCell.path
                                                )
                                            ).build(),
                                            contentDescription = blockInCell.altText,
                                            contentScale = ContentScale.Fit,
                                            modifier = imageModifier
                                        )
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        else -> {
            Timber.w(
                "FlexContainerBlock child type still not supported: ${childBlock::class.simpleName}"
            )
        }
    }
}

@OptIn(ExperimentalFoundationApi::class)
private fun Modifier.realisticBookPage(
    pagerState: PagerState,
    pageIndex: Int,
    paperColor: Color,
    isDarkTheme: Boolean,
    touchY: Float?
): Modifier = composed {
    val frontPath = remember { Path() }
    val backPath = remember { Path() }
    val reflectedScreenPath = remember { Path() }

    this
        .graphicsLayer {
            val pageOffset = (pageIndex - pagerState.currentPage) - pagerState.currentPageOffsetFraction

            if (pageOffset <= 1f && pageOffset > -1f) {
                translationX = -pageOffset * size.width
            }

            if (pageOffset != 0f) {
                shadowElevation = 10f
                shape = androidx.compose.ui.graphics.RectangleShape
                clip = false
            }
        }
        .drawWithContent {
            val pageOffset = (pageIndex - pagerState.currentPage) - pagerState.currentPageOffsetFraction

            if (abs(pageOffset) < 0.001f) {
                drawRect(color = paperColor)
                drawContent()
            }
            else if (pageOffset < 0f && pageOffset > -1f) {
                val progress = -pageOffset
                val w = size.width
                val h = size.height

                val startY = touchY ?: h
                val centerDist = ((startY - h / 2f) / (h / 2f)).coerceIn(-1f, 1f)

                val cornerY = if (centerDist >= 0) h else 0f

                val dragX = w - w * 2.2f * progress
                val dragY = cornerY - h * 0.5f * progress * centerDist

                val midX = (w + dragX) / 2f
                val midY = (cornerY + dragY) / 2f

                val dx = w - dragX
                val dy = cornerY - dragY
                val nLen = kotlin.math.sqrt(dx * dx + dy * dy)

                if (nLen > 0f) {
                    val nx = dx / nLen
                    val ny = dy / nLen

                    val huge = w * 3f
                    val vx = -ny

                    val p1X = midX + vx * huge
                    val p1Y = midY + nx * huge
                    val p2X = midX - vx * huge
                    val p2Y = midY - nx * huge

                    frontPath.rewind()
                    frontPath.moveTo(p1X, p1Y)
                    frontPath.lineTo(p2X, p2Y)
                    frontPath.lineTo(p2X - nx * huge, p2Y - ny * huge)
                    frontPath.lineTo(p1X - nx * huge, p1Y - ny * huge)
                    frontPath.close()

                    clipPath(frontPath) {
                        drawRect(color = paperColor)
                        this@drawWithContent.drawContent()
                    }

                    val shadowWidth = (40.dp.toPx() * (1f - progress)).coerceAtLeast(10.dp.toPx())
                    backPath.rewind()
                    backPath.moveTo(p1X, p1Y)
                    backPath.lineTo(p2X, p2Y)
                    backPath.lineTo(p2X + nx * huge, p2Y + ny * huge)
                    backPath.lineTo(p1X + nx * huge, p1Y + ny * huge)
                    backPath.close()

                    val dropShadowBrush = Brush.linearGradient(
                        colors = listOf(Color.Black.copy(alpha = 0.4f), Color.Transparent),
                        start = Offset(midX, midY),
                        end = Offset(midX + nx * shadowWidth, midY + ny * shadowWidth)
                    )
                    clipRect(0f, 0f, w, h) {
                        drawPath(backPath, dropShadowBrush)
                    }

                    fun reflect(px: Float, py: Float): Offset {
                        val vX = px - midX
                        val vY = py - midY
                        val dist = vX * nx + vY * ny
                        return Offset(px - 2 * dist * nx, py - 2 * dist * ny)
                    }

                    val rTL = reflect(0f, 0f)
                    val rTR = reflect(w, 0f)
                    val rBR = reflect(w, h)
                    val rBL = reflect(0f, h)

                    reflectedScreenPath.rewind()
                    reflectedScreenPath.moveTo(rTL.x, rTL.y)
                    reflectedScreenPath.lineTo(rTR.x, rTR.y)
                    reflectedScreenPath.lineTo(rBR.x, rBR.y)
                    reflectedScreenPath.lineTo(rBL.x, rBL.y)
                    reflectedScreenPath.close()

                    clipRect(0f, 0f, w, h) {
                        clipPath(frontPath) {
                            drawPath(reflectedScreenPath, color = paperColor)

                            val flapTint = if (isDarkTheme) Color.White.copy(alpha = 0.08f) else Color.Black.copy(alpha = 0.06f)
                            drawPath(reflectedScreenPath, color = flapTint)

                            val innerShadowWidth = shadowWidth * 0.7f
                            val innerShadowBrush = Brush.linearGradient(
                                colors = listOf(
                                    Color.Black.copy(alpha = 0.25f),
                                    Color.Black.copy(alpha = 0.05f),
                                    Color.Transparent
                                ),
                                start = Offset(midX, midY),
                                end = Offset(midX - nx * innerShadowWidth, midY - ny * innerShadowWidth)
                            )
                            drawPath(reflectedScreenPath, innerShadowBrush)

                            drawPath(
                                path = reflectedScreenPath,
                                color = if (isDarkTheme) Color.White.copy(alpha = 0.15f) else Color.Black.copy(alpha = 0.15f),
                                style = Stroke(width = 1.dp.toPx())
                            )
                        }

                        drawLine(
                            color = if (isDarkTheme) Color.White.copy(alpha = 0.1f) else Color.Black.copy(alpha = 0.1f),
                            start = Offset(p1X, p1Y),
                            end = Offset(p2X, p2Y),
                            strokeWidth = 1.dp.toPx()
                        )
                    }

                } else {
                    drawRect(color = paperColor)
                    drawContent()
                }
            }
            else if (pageOffset > 0f && pageOffset <= 1f) {
                drawRect(color = paperColor)
                drawContent()
                val dimAlpha = (0.25f * pageOffset).coerceIn(0f, 0.4f)
                drawRect(color = Color.Black.copy(alpha = dimAlpha))
            }
            else {
                drawRect(color = paperColor)
                drawContent()
            }
        }
}

@Suppress("KotlinConstantConditions")
@Composable
fun Modifier.drawCssBorders(
    blockStyle: BlockStyle,
    @Suppress("unused") density: Density
): Modifier = this.drawBehind {
    val topWidth = blockStyle.borderTop?.width?.toPx() ?: 0f
    val rightWidth = blockStyle.borderRight?.width?.toPx() ?: 0f
    val bottomWidth = blockStyle.borderBottom?.width?.toPx() ?: 0f
    val leftWidth = blockStyle.borderLeft?.width?.toPx() ?: 0f

    val tlRadius = blockStyle.borderTopLeftRadius.toPx()
    val trRadius = blockStyle.borderTopRightRadius.toPx()
    val brRadius = blockStyle.borderBottomRightRadius.toPx()
    val blRadius = blockStyle.borderBottomLeftRadius.toPx()

    if (blockStyle.backgroundColor.isSpecified && blockStyle.backgroundColor != Color.Transparent) {
        val bgPath = Path().apply {
            addRoundRect(
                androidx.compose.ui.geometry.RoundRect(
                    rect = size.toRect(),
                    topLeft = androidx.compose.ui.geometry.CornerRadius(tlRadius, tlRadius),
                    topRight = androidx.compose.ui.geometry.CornerRadius(trRadius, trRadius),
                    bottomRight = androidx.compose.ui.geometry.CornerRadius(brRadius, brRadius),
                    bottomLeft = androidx.compose.ui.geometry.CornerRadius(blRadius, blRadius)
                )
            )
        }
        drawPath(bgPath, color = blockStyle.backgroundColor, style = Fill)
    }

    // 2. Helper for PathEffects
    fun getPathEffect(style: String?, width: Float): PathEffect? {
        return when (style) {
            "dashed" -> PathEffect.dashPathEffect(floatArrayOf(width * 3f, width * 2f), 0f)
            "dotted" -> PathEffect.dashPathEffect(floatArrayOf(width, width), 0f)
            else -> null
        }
    }

    // TOP
    if (topWidth > 0f && blockStyle.borderTop != null) {
        val color = blockStyle.borderTop.color
        val effect = getPathEffect(blockStyle.borderTop.style, topWidth)
        val offset = topWidth / 2f

        val startX = if (tlRadius > 0) tlRadius else 0f
        val endX = if (trRadius > 0) size.width - trRadius else size.width

        drawLine(
            color = color,
            start = Offset(startX, offset),
            end = Offset(endX, offset),
            strokeWidth = topWidth,
            pathEffect = effect
        )
    }

    // BOTTOM
    if (bottomWidth > 0f && blockStyle.borderBottom != null) {
        val color = blockStyle.borderBottom.color
        val effect = getPathEffect(blockStyle.borderBottom.style, bottomWidth)
        val offset = size.height - (bottomWidth / 2f)

        val startX = if (blRadius > 0) blRadius else 0f
        val endX = if (brRadius > 0) size.width - brRadius else size.width

        drawLine(
            color = color,
            start = Offset(startX, offset),
            end = Offset(endX, offset),
            strokeWidth = bottomWidth,
            pathEffect = effect
        )
    }

    // LEFT
    if (leftWidth > 0f && blockStyle.borderLeft != null) {
        val color = blockStyle.borderLeft.color
        val effect = getPathEffect(blockStyle.borderLeft.style, leftWidth)
        val offset = leftWidth / 2f

        val startY = if (tlRadius > 0) tlRadius else 0f
        val endY = if (blRadius > 0) size.height - blRadius else size.height

        drawLine(
            color = color,
            start = Offset(offset, startY),
            end = Offset(offset, endY),
            strokeWidth = leftWidth,
            pathEffect = effect
        )
    }

    // RIGHT
    if (rightWidth > 0f && blockStyle.borderRight != null) {
        val color = blockStyle.borderRight.color
        val effect = getPathEffect(blockStyle.borderRight.style, rightWidth)
        val offset = size.width - (rightWidth / 2f)

        val startY = if (trRadius > 0) trRadius else 0f
        val endY = if (brRadius > 0) size.height - brRadius else size.height

        drawLine(
            color = color,
            start = Offset(offset, startY),
            end = Offset(offset, endY),
            strokeWidth = rightWidth,
            pathEffect = effect
        )
    }

    if (tlRadius > 0f && topWidth > 0f && leftWidth > 0f && blockStyle.borderTop != null) {
        drawArc(
            color = blockStyle.borderTop.color,
            startAngle = 180f, sweepAngle = 90f,
            useCenter = false,
            topLeft = Offset(leftWidth/2f, topWidth/2f),
            size = androidx.compose.ui.geometry.Size(tlRadius * 2 - leftWidth, tlRadius * 2 - topWidth),
            style = Stroke(width = topWidth)
        )
    }

    if (trRadius > 0f && topWidth > 0f && rightWidth > 0f && blockStyle.borderTop != null) {
        drawArc(
            color = blockStyle.borderTop.color,
            startAngle = 270f, sweepAngle = 90f,
            useCenter = false,
            topLeft = Offset(size.width - (trRadius * 2) + (rightWidth/2f), topWidth/2f),
            size = androidx.compose.ui.geometry.Size(trRadius * 2 - rightWidth, trRadius * 2 - topWidth),
            style = Stroke(width = topWidth)
        )
    }

    if (brRadius > 0f && bottomWidth > 0f && rightWidth > 0f && blockStyle.borderBottom != null) {
        drawArc(
            color = blockStyle.borderBottom.color,
            startAngle = 0f, sweepAngle = 90f,
            useCenter = false,
            topLeft = Offset(size.width - (brRadius * 2) + (rightWidth/2f), size.height - (brRadius * 2) + (bottomWidth/2f)),
            size = androidx.compose.ui.geometry.Size(brRadius * 2 - rightWidth, brRadius * 2 - bottomWidth),
            style = Stroke(width = bottomWidth)
        )
    }

    if (blRadius > 0f && bottomWidth > 0f && leftWidth > 0f && blockStyle.borderBottom != null) {
        drawArc(
            color = blockStyle.borderBottom.color,
            startAngle = 90f, sweepAngle = 90f,
            useCenter = false,
            topLeft = Offset(leftWidth/2f, size.height - (blRadius * 2) + (bottomWidth/2f)),
            size = androidx.compose.ui.geometry.Size(blRadius * 2 - leftWidth, blRadius * 2 - bottomWidth),
            style = Stroke(width = bottomWidth)
        )
    }
}