package com.rosetta.messenger.ui.chats.calls

import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.filled.CallEnd
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.MicOff
import androidx.compose.material.icons.filled.Videocam
import androidx.compose.material.icons.filled.VideocamOff
import androidx.compose.material.icons.filled.VolumeOff
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.QrCode2
import androidx.compose.material.icons.filled.VolumeUp
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.rosetta.messenger.network.CallPhase
import com.rosetta.messenger.network.CallUiState
import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.ui.components.AvatarImage
import com.rosetta.messenger.ui.components.VerifiedBadge
import com.rosetta.messenger.data.MessageRepository
import compose.icons.TablerIcons
import compose.icons.tablericons.ChevronDown

// ── Call colors ──────────────────────────────────────────────────

private val CallBaseBg = Color(0xFF0D0D14)
private val AcceptGreen = Color(0xFF4CC764)
private val DeclineRed = Color(0xFFE74C3C)
private val ButtonBg = Color.White.copy(alpha = 0.15f)
private val ButtonBgActive = Color.White.copy(alpha = 0.30f)
private val RingColor1 = Color.White.copy(alpha = 0.06f)
private val RingColor2 = Color.White.copy(alpha = 0.10f)
private val RingColor3 = Color.White.copy(alpha = 0.04f)

// Avatar color palette (Mantine colors like iOS)
private val AvatarCallColors = listOf(
    Color(0xFF228BE6), Color(0xFF15AABF), Color(0xFFBE4BDB),
    Color(0xFF40C057), Color(0xFF4C6EF5), Color(0xFF82C91E),
    Color(0xFFFD7E14), Color(0xFFE64980), Color(0xFFFA5252),
    Color(0xFF12B886), Color(0xFF7950F2)
)

private fun callColorForKey(publicKey: String): Color {
    val hash = publicKey.sumOf { it.code }
    return AvatarCallColors[hash % AvatarCallColors.size]
}

/** Animated gradient background with 3 moving blobs — iOS parity */
@Composable
private fun CallGradientBackground(peerPublicKey: String) {
    val primary = remember(peerPublicKey) { callColorForKey(peerPublicKey) }

    // Hue-shifted + darkened variants
    val blob2Color = remember(primary) {
        val hsv = FloatArray(3)
        android.graphics.Color.colorToHSV(
            android.graphics.Color.argb(255,
                (primary.red * 255).toInt(),
                (primary.green * 255).toInt(),
                (primary.blue * 255).toInt()
            ), hsv
        )
        hsv[0] = (hsv[0] + 25f) % 360f
        hsv[2] = hsv[2] * 0.7f
        Color(android.graphics.Color.HSVToColor(hsv))
    }
    val blob3Color = remember(primary) {
        val hsv = FloatArray(3)
        android.graphics.Color.colorToHSV(
            android.graphics.Color.argb(255,
                (primary.red * 255).toInt(),
                (primary.green * 255).toInt(),
                (primary.blue * 255).toInt()
            ), hsv
        )
        hsv[2] = hsv[2] * 0.27f
        Color(android.graphics.Color.HSVToColor(hsv))
    }

    val infiniteTransition = rememberInfiniteTransition(label = "callBg")
    val time by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 1000f,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 1000_000, easing = LinearEasing),
            repeatMode = RepeatMode.Restart
        ),
        label = "time"
    )

    Canvas(modifier = Modifier.fillMaxSize()) {
        val w = size.width
        val h = size.height
        val maxDim = maxOf(w, h)
        val t = time

        // Base dark fill
        drawRect(CallBaseBg)

        // Blob 1 — primary color
        val x1 = w * 0.5f + w * 0.3f * kotlin.math.sin(t * 0.23f)
        val y1 = h * 0.35f + h * 0.2f * kotlin.math.sin(t * 0.19f + 1f)
        drawCircle(
            brush = Brush.radialGradient(
                colors = listOf(
                    primary.copy(alpha = 0.55f),
                    primary.copy(alpha = 0.22f),
                    Color.Transparent
                ),
                center = Offset(x1, y1),
                radius = maxDim * 0.7f
            ),
            radius = maxDim * 0.7f,
            center = Offset(x1, y1)
        )

        // Blob 2 — hue shifted
        val x2 = w * 0.4f + w * 0.35f * kotlin.math.sin(t * 0.17f + 2f)
        val y2 = h * 0.55f + h * 0.25f * kotlin.math.sin(t * 0.21f + 3.5f)
        drawCircle(
            brush = Brush.radialGradient(
                colors = listOf(
                    blob2Color.copy(alpha = 0.45f),
                    blob2Color.copy(alpha = 0.18f),
                    Color.Transparent
                ),
                center = Offset(x2, y2),
                radius = maxDim * 0.65f
            ),
            radius = maxDim * 0.65f,
            center = Offset(x2, y2)
        )

        // Blob 3 — dark
        val x3 = w * 0.6f + w * 0.3f * kotlin.math.sin(t * 0.29f + 4f)
        val y3 = h * 0.7f + h * 0.15f * kotlin.math.sin(t * 0.15f + 5.5f)
        drawCircle(
            brush = Brush.radialGradient(
                colors = listOf(
                    blob3Color.copy(alpha = 0.5f),
                    blob3Color.copy(alpha = 0.2f),
                    Color.Transparent
                ),
                center = Offset(x3, y3),
                radius = maxDim * 0.6f
            ),
            radius = maxDim * 0.6f,
            center = Offset(x3, y3)
        )
    }
}

// ── Main Call Screen ─────────────────────────────────────────────

@Composable
fun CallOverlay(
    state: CallUiState,
    isDarkTheme: Boolean,
    avatarRepository: AvatarRepository? = null,
    isExpanded: Boolean = true,
    onAccept: () -> Unit,
    onDecline: () -> Unit,
    onEnd: () -> Unit,
    onToggleMute: () -> Unit,
    onToggleSpeaker: () -> Unit,
    onMinimize: (() -> Unit)? = null
) {
    var lastVisibleState by remember { mutableStateOf<CallUiState?>(null) }
    if (state.isVisible) {
        lastVisibleState = state
    }
    val uiState = if (state.isVisible) state else (lastVisibleState ?: state)

    val view = LocalView.current
    LaunchedEffect(state.isVisible) {
        if (state.isVisible && !view.isInEditMode) {
            val window = (view.context as android.app.Activity).window
            window.statusBarColor = android.graphics.Color.TRANSPARENT
            window.navigationBarColor = android.graphics.Color.TRANSPARENT
            val ctrl = androidx.core.view.WindowCompat.getInsetsController(window, view)
            ctrl.isAppearanceLightStatusBars = false
            ctrl.isAppearanceLightNavigationBars = false
        }
    }

    AnimatedVisibility(
        visible = state.isVisible && isExpanded,
        enter = fadeIn(tween(300)),
        exit = fadeOut(tween(200))
    ) {
        Box(
            modifier = Modifier.fillMaxSize()
        ) {
            // Animated gradient background (iOS parity)
            CallGradientBackground(peerPublicKey = uiState.peerPublicKey)
            // ── Top controls: minimize (left) + key cast QR (right) ──
            val canMinimize = onMinimize != null && uiState.phase != CallPhase.INCOMING && uiState.phase != CallPhase.IDLE
            val showKeyCast =
                (uiState.phase == CallPhase.ACTIVE || uiState.phase == CallPhase.CONNECTING) &&
                    uiState.keyCast.isNotBlank()

            if (canMinimize || showKeyCast) {
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .align(Alignment.TopCenter)
                        .statusBarsPadding()
                        .padding(horizontal = 12.dp, vertical = 8.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    if (canMinimize) {
                        IconButton(
                            onClick = { onMinimize?.invoke() },
                            modifier = Modifier.size(44.dp)
                        ) {
                            Icon(
                                imageVector = TablerIcons.ChevronDown,
                                contentDescription = "Minimize call",
                                tint = Color.White,
                                modifier = Modifier.size(26.dp)
                            )
                        }
                    } else {
                        Spacer(modifier = Modifier.size(48.dp))
                    }

                    if (showKeyCast) {
                        EncryptionKeyButton(keyHex = uiState.keyCast)
                    } else {
                        Spacer(modifier = Modifier.size(48.dp))
                    }
                }
            }

            // ── Center content: rings + avatar + name + status ──
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .align(Alignment.TopCenter)
                    .statusBarsPadding()
                    .padding(top = 100.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                // Avatar with rings
                CallAvatar(
                    peerPublicKey = uiState.peerPublicKey,
                    displayName = uiState.displayName,
                    avatarRepository = avatarRepository,
                    isDarkTheme = isDarkTheme,
                    showRings = uiState.phase != CallPhase.IDLE
                )

                Spacer(modifier = Modifier.height(24.dp))

                // Name
                Row(
                    modifier = Modifier.padding(horizontal = 48.dp),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.Center
                ) {
                    Text(
                        text = uiState.displayName,
                        color = Color.White,
                        fontSize = 26.sp,
                        fontWeight = FontWeight.SemiBold,
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis,
                        modifier = Modifier.weight(1f, fill = false)
                    )
                    val isOfficialByKey = MessageRepository.isSystemAccount(uiState.peerPublicKey)
                    if (isOfficialByKey) {
                        Spacer(modifier = Modifier.width(6.dp))
                        VerifiedBadge(
                            verified = 1,
                            size = 20,
                            isDarkTheme = true
                        )
                    }
                }

                Spacer(modifier = Modifier.height(6.dp))

                // Status with animated dots
                val showDots = uiState.phase == CallPhase.OUTGOING ||
                        uiState.phase == CallPhase.CONNECTING ||
                        uiState.phase == CallPhase.INCOMING

                if (showDots) {
                    AnimatedDotsText(
                        baseText = when (uiState.phase) {
                            CallPhase.OUTGOING -> uiState.statusText.ifBlank { "Requesting" }
                            CallPhase.CONNECTING -> uiState.statusText.ifBlank { "Connecting" }
                            CallPhase.INCOMING -> "Ringing"
                            else -> ""
                        },
                        color = Color.White.copy(alpha = 0.6f)
                    )
                } else if (uiState.phase == CallPhase.ACTIVE) {
                    Text(
                        text = formatCallDuration(uiState.durationSec),
                        color = Color.White.copy(alpha = 0.6f),
                        fontSize = 15.sp
                    )
                }

            }

            // ── Bottom buttons ──
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .align(Alignment.BottomCenter)
                    .navigationBarsPadding()
                    .padding(bottom = 32.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                AnimatedContent(
                    targetState = uiState.phase,
                    transitionSpec = {
                        (fadeIn(tween(200)) + slideInVertically { it / 3 }) togetherWith
                                (fadeOut(tween(150)) + slideOutVertically { it / 3 })
                    },
                    label = "btns"
                ) { phase ->
                    when (phase) {
                        CallPhase.INCOMING -> IncomingButtons(onAccept, onDecline)
                        CallPhase.ACTIVE -> ActiveButtons(uiState, onToggleMute, onToggleSpeaker, onEnd)
                        CallPhase.OUTGOING, CallPhase.CONNECTING -> OutgoingButtons(uiState, onToggleSpeaker, onToggleMute, onEnd)
                        CallPhase.IDLE -> Spacer(Modifier.height(1.dp))
                    }
                }
            }
        }
    }
}

// ── Avatar with concentric rings ─────────────────────────────────

@Composable
private fun CallAvatar(
    peerPublicKey: String,
    displayName: String,
    avatarRepository: AvatarRepository?,
    isDarkTheme: Boolean,
    showRings: Boolean
) {
    val avatarSize = 130.dp
    val ringPadding = 50.dp
    val totalSize = avatarSize + ringPadding * 2

    val infiniteTransition = rememberInfiniteTransition(label = "rings")
    val ringScale by infiniteTransition.animateFloat(
        1f, 1.08f,
        infiniteRepeatable(tween(3000, easing = EaseInOut), RepeatMode.Reverse),
        label = "ringScale"
    )

    val ringAlpha by animateFloatAsState(
        if (showRings) 1f else 0f, tween(400), label = "ringAlpha"
    )

    Box(modifier = Modifier.size(totalSize), contentAlignment = Alignment.Center) {
        // Concentric rings (like Telegram)
        if (ringAlpha > 0f) {
            // Outer ring
            Box(
                modifier = Modifier
                    .size(avatarSize + 44.dp)
                    .scale(ringScale)
                    .graphicsLayer { alpha = ringAlpha }
                    .clip(CircleShape)
                    .background(RingColor3)
            )
            // Middle ring
            Box(
                modifier = Modifier
                    .size(avatarSize + 28.dp)
                    .scale(ringScale * 0.98f)
                    .graphicsLayer { alpha = ringAlpha }
                    .clip(CircleShape)
                    .background(RingColor1)
            )
            // Inner ring
            Box(
                modifier = Modifier
                    .size(avatarSize + 14.dp)
                    .scale(ringScale * 0.96f)
                    .graphicsLayer { alpha = ringAlpha }
                    .clip(CircleShape)
                    .background(RingColor2)
            )
        }

        // Avatar
        AvatarImage(
            publicKey = peerPublicKey,
            avatarRepository = avatarRepository,
            size = avatarSize,
            isDarkTheme = isDarkTheme,
            showOnlineIndicator = false,
            isOnline = false,
            displayName = displayName
        )
    }
}

// ── Incoming: Accept + Decline ───────────────────────────────────

@Composable
private fun IncomingButtons(onAccept: () -> Unit, onDecline: () -> Unit) {
    Row(
        modifier = Modifier.fillMaxWidth().padding(horizontal = 60.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        CallButton(DeclineRed, "Decline", Icons.Default.CallEnd, onClick = onDecline)
        CallButton(AcceptGreen, "Accept", Icons.Default.Call, onClick = onAccept, showPulse = true)
    }
}

// ── Outgoing/Connecting: Speaker + Mute + End Call ───────────────

@Composable
private fun OutgoingButtons(
    state: CallUiState, onSpeaker: () -> Unit, onMute: () -> Unit, onEnd: () -> Unit
) {
    Row(
        modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp),
        horizontalArrangement = Arrangement.SpaceEvenly
    ) {
        CallButton(
            if (state.isSpeakerOn) ButtonBgActive else ButtonBg, "Speaker",
            if (state.isSpeakerOn) Icons.Default.VolumeUp else Icons.Default.VolumeOff,
            onClick = onSpeaker
        )
        CallButton(
            if (state.isMuted) ButtonBgActive else ButtonBg, "Mute",
            if (state.isMuted) Icons.Default.MicOff else Icons.Default.Mic,
            onClick = onMute
        )
        CallButton(DeclineRed, "End Call", Icons.Default.CallEnd, onClick = onEnd)
    }
}

// ── Active: Speaker + Video + Mute + End Call ────────────────────

@Composable
private fun ActiveButtons(
    state: CallUiState, onMute: () -> Unit, onSpeaker: () -> Unit, onEnd: () -> Unit
) {
    Row(
        modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp),
        horizontalArrangement = Arrangement.SpaceEvenly
    ) {
        CallButton(
            if (state.isSpeakerOn) ButtonBgActive else ButtonBg, "Speaker",
            if (state.isSpeakerOn) Icons.Default.VolumeUp else Icons.Default.VolumeOff,
            onClick = onSpeaker
        )
        CallButton(
            if (state.isMuted) ButtonBgActive else ButtonBg, "Mute",
            if (state.isMuted) Icons.Default.MicOff else Icons.Default.Mic,
            onClick = onMute
        )
        CallButton(DeclineRed, "End Call", Icons.Default.CallEnd, onClick = onEnd)
    }
}

// ── Reusable round button with icon + label ──────────────────────

@Composable
private fun CallButton(
    color: Color,
    label: String,
    icon: ImageVector,
    size: Dp = 60.dp,
    showPulse: Boolean = false,
    onClick: () -> Unit
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isPressed by interactionSource.collectIsPressedAsState()
    val btnScale by animateFloatAsState(
        if (isPressed) 0.88f else 1f,
        spring(dampingRatio = 0.5f, stiffness = 800f), label = "s"
    )

    val inf = rememberInfiniteTransition(label = "p_$label")
    val pulseScale by inf.animateFloat(
        1f, 1.35f,
        infiniteRepeatable(tween(1200, easing = EaseOut), RepeatMode.Restart), label = "ps"
    )
    val pulseAlpha by inf.animateFloat(
        0.4f, 0f,
        infiniteRepeatable(tween(1200, easing = EaseOut), RepeatMode.Restart), label = "pa"
    )

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.width(72.dp)
    ) {
        Box(contentAlignment = Alignment.Center) {
            if (showPulse) {
                Box(
                    Modifier.size(size).scale(pulseScale)
                        .graphicsLayer { alpha = pulseAlpha }
                        .clip(CircleShape).background(color.copy(alpha = 0.4f))
                )
            }
            Box(
                Modifier.size(size).scale(btnScale).clip(CircleShape).background(color)
                    .clickable(interactionSource, indication = null, onClick = onClick),
                contentAlignment = Alignment.Center
            ) {
                Icon(icon, label, tint = Color.White, modifier = Modifier.size(26.dp))
            }
        }
        Spacer(Modifier.height(8.dp))
        Text(
            label, color = Color.White.copy(alpha = 0.7f), fontSize = 11.sp,
            maxLines = 1, textAlign = TextAlign.Center
        )
    }
}

// ── Animated dots (Canvas circles with staggered scale) ──────────

@Composable
private fun AnimatedDotsText(baseText: String, color: Color) {
    val inf = rememberInfiniteTransition(label = "dots")
    val d0 by inf.animateFloat(0f, 1f, infiniteRepeatable(tween(800, easing = LinearEasing)), label = "d0")
    val d1 by inf.animateFloat(0f, 1f, infiniteRepeatable(tween(800, delayMillis = 150, easing = LinearEasing)), label = "d1")
    val d2 by inf.animateFloat(0f, 1f, infiniteRepeatable(tween(800, delayMillis = 300, easing = LinearEasing)), label = "d2")

    val density = LocalDensity.current
    val dotR = with(density) { 2.dp.toPx() }
    val spacing = with(density) { 6.dp.toPx() }

    Row(verticalAlignment = Alignment.CenterVertically) {
        Text(baseText, color = color, fontSize = 15.sp)
        Spacer(Modifier.width(5.dp))
        Canvas(Modifier.size(width = 22.dp, height = 14.dp)) {
            val cy = size.height / 2
            listOf(d0, d1, d2).forEachIndexed { i, p ->
                val s = if (p < 0.4f) {
                    val t = p / 0.4f; t * t * (3f - 2f * t)
                } else {
                    val t = (p - 0.4f) / 0.6f; 1f - t * t * (3f - 2f * t)
                }
                drawCircle(
                    color.copy(alpha = 0.4f + 0.6f * s),
                    radius = dotR * (0.5f + 0.5f * s),
                    center = Offset(dotR + i * spacing, cy)
                )
            }
        }
    }
}

// ── Helpers ──────────────────────────────────────────────────────

private fun formatCallDuration(seconds: Int): String {
    val s = seconds.coerceAtLeast(0)
    val h = s / 3600; val m = (s % 3600) / 60; val sec = s % 60
    return if (h > 0) "%d:%02d:%02d".format(h, m, sec) else "%02d:%02d".format(m, sec)
}

/**
 * QR icon in top-right corner — tap to show encryption key dropdown.
 * 1:1 match with Desktop's IconQrcode + Popover.
 */
@Composable
private fun EncryptionKeyButton(keyHex: String) {
    var showPopup by remember { mutableStateOf(false) }

    Box {
        // QR code icon (matches Desktop IconQrcode size={24} color="white")
        Icon(
            imageVector = Icons.Default.QrCode2,
            contentDescription = "Encryption key",
            tint = Color.White,
            modifier = Modifier
                .size(24.dp)
                .clickable { showPopup = !showPopup }
        )

        // Dropdown popover (matches Desktop Popover width={300} withArrow)
        androidx.compose.material3.DropdownMenu(
            expanded = showPopup,
            onDismissRequest = { showPopup = false },
            modifier = Modifier
                .widthIn(max = 300.dp)
                .background(Color(0xFF1E293B)),
        ) {
            Column(modifier = Modifier.padding(12.dp)) {
                Row(
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(12.dp)
                ) {
                    Text(
                        text = "This call is secured by 256 bit end-to-end encryption. " +
                                "Only you and the recipient can read or listen to the content of this call.",
                        color = Color.White.copy(alpha = 0.6f),
                        fontSize = 11.sp,
                        lineHeight = 15.sp,
                        modifier = Modifier.weight(1f)
                    )
                    Canvas(modifier = Modifier.size(80.dp)) {
                        drawKeyGrid(keyHex, size.width, this)
                    }
                }
                Spacer(modifier = Modifier.height(8.dp))
                val clipboardManager = androidx.compose.ui.platform.LocalClipboardManager.current
                var copied by remember { mutableStateOf(false) }
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clip(androidx.compose.foundation.shape.RoundedCornerShape(8.dp))
                        .background(Color.White.copy(alpha = 0.08f))
                        .clickable {
                            clipboardManager.setText(androidx.compose.ui.text.AnnotatedString(keyHex))
                            copied = true
                        }
                        .padding(horizontal = 10.dp, vertical = 8.dp),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(6.dp)
                ) {
                    Icon(
                        imageVector = if (copied) Icons.Default.Check else Icons.Default.ContentCopy,
                        contentDescription = "Copy key",
                        tint = Color.White.copy(alpha = 0.5f),
                        modifier = Modifier.size(14.dp)
                    )
                    Text(
                        text = if (copied) "Copied!" else keyHex.take(16) + "..." + keyHex.takeLast(8),
                        color = Color.White.copy(alpha = 0.4f),
                        fontSize = 10.sp,
                        maxLines = 1,
                        fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace
                    )
                }
            }
        }
    }
}

/** Palette matching Desktop's Mantine theme.colors.blue[1..5] */
private val KeyGridPalette = listOf(
    Color(0xFFDBE4FF), // blue[1]
    Color(0xFFBAC8FF), // blue[2]
    Color(0xFF91A7FF), // blue[3]
    Color(0xFF748FFC), // blue[4]
    Color(0xFF5C7CFA), // blue[5]
)

/**
 * Draw 8x8 color grid — same algorithm as Desktop KeyImage.tsx:
 * each character's charCode % palette.size determines the color.
 */
private fun drawKeyGrid(keyHex: String, totalSize: Float, scope: androidx.compose.ui.graphics.drawscope.DrawScope) {
    val cells = 8
    val cellSize = totalSize / cells
    for (i in 0 until cells * cells) {
        val color = if (i < keyHex.length) {
            KeyGridPalette[keyHex[i].code % KeyGridPalette.size]
        } else {
            KeyGridPalette[0]
        }
        val row = i / cells
        val col = i % cells
        scope.drawRect(
            color = color,
            topLeft = Offset(col * cellSize, row * cellSize),
            size = androidx.compose.ui.geometry.Size(cellSize, cellSize)
        )
    }
}
