package com.rosetta.messenger.ui.components.metaball

import android.graphics.ColorMatrixColorFilter
import android.graphics.Path
import android.graphics.RectF

import android.graphics.RenderEffect
import android.graphics.Shader
import android.os.Build
import android.view.Gravity
import androidx.annotation.RequiresApi
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape

import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect

import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView

import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
import androidx.compose.ui.graphics.Color as ComposeColor

/**
 * Constants for the Profile Metaball Animation
 * Based on Telegram's ProfileMetaballView implementation
 * 
 * THRESHOLDS from Telegram:
 * - isDrawing: vr <= 40dp (start metaball effect)
 * - isNear: vr <= 32dp (avatar very close to notch)
 * - Form transition: 40dp → 34dp (circle → rounded rect)
 * - Alpha fade: 32dp → 18dp (1.0 → 0.0)
 * 
 * SCALE from Telegram:
 * - Collapsed (into notch): avatarScale = lerp(24dp, 96dp, diff) / 100f
 * - Expanded (pull-down): avatarScale = lerp(96/42, 138/42, expandProgress*3) / 100f * 42
 * 
 * POSITION from Telegram:
 * - avatarY = lerp(endY, startY, diff)
 * - endY = notch center or -dp(29)
 * - startY = statusBar + actionBarHeight - dp(21)
 */
object ProfileMetaballConstants {
    // Avatar dimensions (like Telegram: collapsed into 24dp, normal 96dp, expanded 138dp)
    val AVATAR_SIZE_COLLAPSED = 24.dp   // intoSize when fully collapsed into notch
    val AVATAR_SIZE_NORMAL = 96.dp      // Normal expanded avatar size  
    val AVATAR_SIZE_PULLED = 138.dp     // When pulled down (isPulledDown)
    
    // Legacy - for compatibility
    val AVATAR_SIZE_EXPANDED = 120.dp
    val AVATAR_SIZE_MIN = 24.dp
    
    // Standard ActionBar height (like Telegram's actionBar.getHeight())
    val ACTION_BAR_HEIGHT = 56.dp
    
    // Offset from actionBar bottom to avatar center (Telegram: -dp(21))
    val AVATAR_Y_OFFSET = 21.dp
    
    // Telegram thresholds (in dp)
    val THRESHOLD_DRAWING = 40.dp   // isDrawing = vr <= 40dp
    val THRESHOLD_NEAR = 32.dp      // isNear = vr <= 32dp
    val THRESHOLD_SHAPE_START = 40.dp  // Start shape transition
    val THRESHOLD_SHAPE_END = 34.dp    // End shape transition (fully rounded rect)
    val THRESHOLD_ALPHA_START = 32.dp  // Start alpha fade
    val THRESHOLD_ALPHA_END = 10.dp    // Extended from 18dp for smoother fade-out
    
    // Corner radius for rounded rect form (like Telegram's dp(22))
    val ROUNDED_RECT_RADIUS = 22.dp
    
    // Animation thresholds  
    const val MERGE_START_PROGRESS = 0.5f
    const val MERGE_COMPLETE_PROGRESS = 0.99f  // Let opacity handle fade, don't cut off early
    
    // Slightly stronger blur because our avatar base size is larger than Telegram's 96dp.
    const val BLUR_RADIUS = 18f
    
    // Blur range for avatar (like Telegram: 2 + (1-fraction) * 20)
    const val BLUR_MIN = 2f
    const val BLUR_MAX = 22f
    
    // Fallback camera size if no notch detected (like status bar rect in Telegram)
    val FALLBACK_CAMERA_SIZE = 12.dp
    
    // Y position offset when fully collapsed (Telegram: -dp(29))
    val COLLAPSED_Y_OFFSET = (-29).dp
}

/**
 * GPU alpha threshold via ColorMatrixColorFilter (API 31+)
 * Replaces AGSL RuntimeShader — same gooey effect, works on Android 12+
 *
 * From Telegram's ProfileGooeyView.GPUImpl:
 * Alpha row: newAlpha = oldAlpha * 51 - 6375
 * Values above ~125/255 become opaque, below become transparent.
 * Equivalent to AGSL cutoff=0.5 but without requiring API 33.
 */
private val GPU_ALPHA_THRESHOLD_MATRIX = floatArrayOf(
    1f, 0f, 0f, 0f, 0f,
    0f, 1f, 0f, 0f, 0f,
    0f, 0f, 1f, 0f, 0f,
    0f, 0f, 0f, 51f, 51f * -125f
)

/**
 * No-notch fallback: black bar height at the top of the screen.
 * Matches Telegram's BLACK_KING_BAR = 32dp.
 * When no notch is detected, the avatar merges into this bar instead.
 */
private const val BLACK_BAR_HEIGHT_DP = 32f

private fun isCenteredTopCutout(
    notchInfo: NotchInfoUtils.NotchInfo?,
    screenWidthPx: Float
): Boolean {
    if (notchInfo == null || notchInfo.bounds.width() <= 0f || notchInfo.bounds.height() <= 0f) {
        return false
    }
    if (notchInfo.gravity != Gravity.CENTER) {
        return false
    }
    val tolerancePx = screenWidthPx * 0.10f
    return abs(notchInfo.bounds.centerX() - screenWidthPx / 2f) <= tolerancePx
}

private fun resolveSafeNotchCenterY(
    notchInfo: NotchInfoUtils.NotchInfo?,
    hasCenteredCutout: Boolean,
    statusBarHeightPx: Float,
    fallbackCenterY: Float
): Float {
    if (!hasCenteredCutout || notchInfo == null) return fallbackCenterY

    val rawCenterY = if (notchInfo.isLikelyCircle) {
        notchInfo.bounds.bottom - min(notchInfo.bounds.width(), notchInfo.bounds.height()) / 2f
    } else {
        notchInfo.bounds.centerY()
    }

    // Some OEM cutout specs report unstable Y; keep collapse target in status-bar safe zone.
    val minSafeY = max(statusBarHeightPx * 0.45f, fallbackCenterY)
    val maxSafeY = max(statusBarHeightPx * 0.95f, minSafeY + 1f)
    return rawCenterY.coerceIn(minSafeY, maxSafeY)
}

/**
 * State for avatar position and size during animation
 * Like Telegram's ProfileMetaballView state variables
 */
private data class AvatarState(
    val centerX: Float,
    val centerY: Float,
    val radius: Float,           // vr in Telegram
    val opacity: Float,          // alpha / 255f
    val showBlob: Boolean,
    val isDrawing: Boolean,      // vr <= 40dp
    val isNear: Boolean,         // vr <= 32dp
    val cornerRadius: Float,     // For rounded rect transition
    val blurRadius: Float        // Blur amount when near
)

/**
 * Compute avatar state based on collapse progress
 * Exact logic from Telegram's ProfileMetaballView.onDraw() and needLayout()
 * 
 * Key formulas from Telegram:
 * - avatarScale = lerp(intoSize, 96, diff) / 100f  (where intoSize = 24 or notch width/2)
 * - avatarY = lerp(endY, startY, diff)  (endY = notch center, startY = statusBar + actionBar - dp(21))
 * - vr = view.getWidth() * view.getScaleX() * 0.5f  (half of scaled avatar width)
 */
private fun computeAvatarState(
    collapseProgress: Float,      // 0 = expanded, 1 = collapsed into notch
    expansionProgress: Float,     // 0 = normal, 1 = pulled down to full screen
    screenWidthPx: Float,
    statusBarHeightPx: Float,
    headerHeightPx: Float,
    avatarSizeExpandedPx: Float,  // Normal avatar size (96dp in Telegram terms)
    avatarSizeMinPx: Float,       // Into notch size (24dp or notch width)
    hasAvatar: Boolean,
    // Notch info
    notchCenterY: Float,
    notchRadiusPx: Float,
    // Telegram thresholds in pixels
    dp40: Float,
    dp34: Float,
    dp32: Float,
    dp18: Float,
    dp22: Float,  // Corner radius for rounded rect
    fullCornerRadius: Float  // Full circle corner radius
): AvatarState {
    // ═══════════════════════════════════════════════════════════════
    // TELEGRAM LOGIC: diff = 1 - collapseProgress (for us)
    // diff = how "expanded" the avatar is (1 = fully expanded, 0 = collapsed)
    // ═══════════════════════════════════════════════════════════════
    val diff = 1f - collapseProgress
    
    // ═══════════════════════════════════════════════════════════════
    // RADIUS (vr in Telegram)
    // Telegram: avatarScale = lerp(intoSize, 96, diff) / 100f
    // Then: vr = view.getWidth() * avatarScale * 0.5f
    // For us: view.getWidth() = avatarSizeExpandedPx (base size)
    // ═══════════════════════════════════════════════════════════════
    val radius: Float = when {
        // Pull-down expansion (like Telegram isPulledDown)
        hasAvatar && expansionProgress > 0f -> {
            // Telegram: avatarScale = lerp(96/42, 138/42, min(1, expandProgress*3)) / 100f * 42f
            val expandScale = lerpFloat(
                avatarSizeExpandedPx / 2f,      // Normal radius
                screenWidthPx / 2f,              // Full screen width / 2
                expansionProgress
            )
            expandScale
        }
        // Collapsing into notch
        collapseProgress > 0f -> {
            // Telegram: avatarScale = lerp(intoSize, 96, diff) / 100f
            // vr = baseWidth * avatarScale * 0.5
            // Keep a minimum collapsed size so tiny punch-hole cameras don't make the blob too small.
            val intoSize = max(avatarSizeMinPx, notchRadiusPx * 2f)
            val normalSize = avatarSizeExpandedPx
            val avatarWidth = lerpFloat(intoSize, normalSize, diff)
            avatarWidth / 2f  // radius = half of width
        }
        // Normal state
        else -> avatarSizeExpandedPx / 2f
    }
    
    // ═══════════════════════════════════════════════════════════════
    // Telegram thresholds
    // ═══════════════════════════════════════════════════════════════
    val isDrawing = radius <= dp40
    val isNear = radius <= dp32
    
    // Always lock X to screen center to avoid OEM cutout offset issues on some devices.
    val centerX: Float = screenWidthPx / 2f
    
    // ═══════════════════════════════════════════════════════════════
    // CENTER Y - Telegram: avatarY = lerp(endY, startY, diff)
    // startY = statusBar + actionBarHeight + avatarRadius - dp(21)
    // endY = notch center (or -dp(29) if no notch)
    // 
    // Telegram формула: startY = statusBarHeight + actionBarHeight - dp(21) + avatarRadius
    // Это помещает центр аватара на dp(21) НИЖЕ нижней границы actionBar
    // ═══════════════════════════════════════════════════════════════
    val actionBarHeightPx = dp40 + dp18  // ~58dp, близко к стандартным 56dp
    val avatarYOffset = dp22  // ~21dp offset ниже actionBar
    val startY = statusBarHeightPx + actionBarHeightPx - avatarYOffset + (avatarSizeExpandedPx / 2f)
    val endY = notchCenterY  // Target = notch center
    
    val centerY: Float = when {
        // Pull-down expansion
        hasAvatar && expansionProgress > 0f -> {
            lerpFloat(startY, headerHeightPx / 2f, expansionProgress)
        }
        // Collapsing - animate Y towards notch
        else -> {
            lerpFloat(endY, startY, diff)
        }
    }
    
    // ═══════════════════════════════════════════════════════════════
    // CORNER RADIUS - Shape transition
    // Telegram: аватарка остаётся КРУГЛОЙ пока тянешь!
    // Квадратной становится только при полном раскрытии (isPulledDown)
    // Переход круг→квадрат начинается при expansionProgress > 0.8
    // ═══════════════════════════════════════════════════════════════
    val cornerRadius: Float = when {
        // EXPANDED - переход к квадрату начинается только после 80% expansion
        expansionProgress > 0f -> {
            // Telegram: квадрат только при полном раскрытии
            // До 80% - остаётся круглым, после 80% - быстро переходит в квадрат
            val squareProgress = ((expansionProgress - 0.8f) / 0.2f).coerceIn(0f, 1f)
            lerpFloat(fullCornerRadius, 0f, squareProgress)
        }
        // COLLAPSING - переход круг → rounded rect (like Telegram)
        isDrawing -> {
            // Telegram: lerp(dp(22), radius, clamp01((vr - dp(34)) / dp(6)))
            val shapeProgress = ((radius - dp34) / (dp40 - dp34)).coerceIn(0f, 1f)
            val roundRadiusCollapse = lerpFloat(dp22, fullCornerRadius, shapeProgress)
            // Telegram: scaledRadius = vr / dp(22) * roundRadiusCollapse
            (radius / dp22) * roundRadiusCollapse
        }
        // NORMAL - полный круг
        else -> fullCornerRadius
    }
    
    // ═══════════════════════════════════════════════════════════════
    // OPACITY - Smooth fade: 32dp → 10dp with ease-out curve
    // Wider range + quadratic easing = much smoother disappearance
    // ═══════════════════════════════════════════════════════════════
    val opacity: Float = if (isNear) {
        val linearFraction = (max(0f, radius - dp18) / (dp32 - dp18)).coerceIn(0f, 1f)
        // Ease-out: starts fast, slows near end → avatar stays visible longer
        val eased = 1f - (1f - linearFraction) * (1f - linearFraction)
        lerpFloat(0f, 1f, eased)
    } else {
        1f
    }
    
    // ═══════════════════════════════════════════════════════════════
    // BLUR RADIUS - Telegram: 2 + (1 - fraction) * 20
    // More blur as avatar gets closer to notch
    // ═══════════════════════════════════════════════════════════════
    val blurRadius: Float = if (isNear) {
        val fraction = max(0f, radius - dp18) / (dp32 - dp18)
        ProfileMetaballConstants.BLUR_MIN + (1f - fraction) * (ProfileMetaballConstants.BLUR_MAX - ProfileMetaballConstants.BLUR_MIN)
    } else {
        0f
    }
    
    val showBlob = collapseProgress < ProfileMetaballConstants.MERGE_COMPLETE_PROGRESS && radius > 1f
    
    // DEBUG LOG
    return AvatarState(
        centerX = centerX,
        centerY = centerY,
        radius = radius,
        opacity = opacity,
        showBlob = showBlob,
        isDrawing = isDrawing,
        isNear = isNear,
        cornerRadius = cornerRadius,
        blurRadius = blurRadius
    )
}

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

private fun easeOutCubic(value: Float): Float {
    val t = value.coerceIn(0f, 1f)
    val inv = 1f - t
    return 1f - inv * inv * inv
}

/**
 * Profile Metaball Effect with real Bezier path - like Telegram!
 * 
 * Creates a liquid "droplet" effect when scrolling up - the avatar
 * stretches and merges with the camera/notch area using smooth curves.
 * 
 * IMPORTANT: Blur is applied ONLY to the metaball shapes, NOT to the avatar content!
 */
@RequiresApi(Build.VERSION_CODES.S)
@Composable
fun ProfileMetaballOverlay(
    collapseProgress: Float,
    expansionProgress: Float,
    statusBarHeight: Dp,
    headerHeight: Dp,
    hasAvatar: Boolean,
    @Suppress("UNUSED_PARAMETER") avatarColor: ComposeColor,
    factorMult: Float = 1f,
    modifier: Modifier = Modifier,
    avatarContent: @Composable BoxScope.() -> Unit = {},
) {
    val context = LocalContext.current
    val view = LocalView.current
    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp.dp
    val density = LocalDensity.current
    
    // Convert to pixels for path calculations
    val screenWidthPx = with(density) { screenWidth.toPx() }
    val statusBarHeightPx = with(density) { statusBarHeight.toPx() }
    val headerHeightPx = with(density) { headerHeight.toPx() }
    val avatarSizeExpandedPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_EXPANDED.toPx() }
    val avatarSizeMinPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_MIN.toPx() }
    
    // Get REAL camera/notch info from system (like Telegram does)
    // Try resource-based first, fallback to DisplayCutout API (for Pixel 7 Pro etc.)
    val notchInfo = remember(view) {
        NotchInfoUtils.getInfo(context) ?: NotchInfoUtils.getInfoFromCutout(view)
    }
    
    val hasCenteredNotch = remember(notchInfo, screenWidthPx) {
        isCenteredTopCutout(notchInfo, screenWidthPx)
    }

    // Calculate notch radius based on real device notch (like Telegram's ProfileGooeyView)
    val notchRadiusPx = remember(notchInfo, hasCenteredNotch) {
        if (hasCenteredNotch && notchInfo != null) {
            if (notchInfo.isLikelyCircle) {
                // Circular camera cutout - use actual width/height
                min(notchInfo.bounds.width(), notchInfo.bounds.height()) / 2f
            } else {
                // Non-circular notch - use max dimension
                kotlin.math.max(notchInfo.bounds.width(), notchInfo.bounds.height()) / 2f
            }
        } else {
            // No notch info - use fallback (small circle like status bar)
            with(density) { ProfileMetaballConstants.FALLBACK_CAMERA_SIZE.toPx() }
        }
    }
    
    // Always use true screen center for X target to keep droplet perfectly centered.
    val notchCenterX = screenWidthPx / 2f
    
    val notchCenterY = remember(notchInfo, hasCenteredNotch, statusBarHeightPx) {
        resolveSafeNotchCenterY(
            notchInfo = notchInfo,
            hasCenteredCutout = hasCenteredNotch,
            statusBarHeightPx = statusBarHeightPx,
            fallbackCenterY = statusBarHeightPx / 2f
        )
    }
    
    // Scale Telegram thresholds to our bigger base avatar size (120dp vs 96dp).
    val thresholdScale = (avatarSizeExpandedPx / with(density) { 96.dp.toPx() }).coerceIn(1f, 1.35f)
    val dp40 = with(density) { ProfileMetaballConstants.THRESHOLD_DRAWING.toPx() } * thresholdScale
    val dp34 = with(density) { ProfileMetaballConstants.THRESHOLD_SHAPE_END.toPx() } * thresholdScale
    val dp32 = with(density) { ProfileMetaballConstants.THRESHOLD_NEAR.toPx() } * thresholdScale
    val dp18 = with(density) { ProfileMetaballConstants.THRESHOLD_ALPHA_END.toPx() } * thresholdScale
    val dp22 = with(density) { ProfileMetaballConstants.ROUNDED_RECT_RADIUS.toPx() } * thresholdScale
    
    // 🔥 FIX: Убраны volatile keys (collapseProgress, expansionProgress) из remember
    // derivedStateOf автоматически отслеживает их как зависимости внутри лямбды
    // Только стабильные параметры (размеры экрана, notch info) как ключи remember
    val avatarState by remember(screenWidthPx, statusBarHeightPx, headerHeightPx, notchCenterX, notchCenterY, notchRadiusPx) {
        derivedStateOf {
            computeAvatarState(
                collapseProgress = collapseProgress,
                expansionProgress = expansionProgress,
                screenWidthPx = screenWidthPx,
                statusBarHeightPx = statusBarHeightPx,
                headerHeightPx = headerHeightPx,
                avatarSizeExpandedPx = avatarSizeExpandedPx,
                avatarSizeMinPx = avatarSizeMinPx,
                hasAvatar = hasAvatar,
                notchCenterY = notchCenterY,
                notchRadiusPx = notchRadiusPx,
                dp40 = dp40,
                dp34 = dp34,
                dp32 = dp32,
                dp18 = dp18,
                dp22 = dp22,
                fullCornerRadius = avatarSizeExpandedPx / 2f  // Full circle = radius
            )
        }
    }
    
    // Path for metaball connector
    val metaballPath = remember { Path() }
    val c1 = remember { Point() } // Notch center
    val c2 = remember { Point() } // Avatar center
    val metaballWorkspace = remember { MetaballWorkspace() }

    // Reusable RectF like Telegram's AndroidUtilities.rectTmp
    val rectTmp = remember { RectF() }
    val blackPaint = remember {
        android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG).apply {
            color = android.graphics.Color.BLACK
            style = android.graphics.Paint.Style.FILL
        }
    }

    // Black bar height for no-notch fallback (Telegram's BLACK_KING_BAR)
    val blackBarHeightPx = with(density) { BLACK_BAR_HEIGHT_DP.dp.toPx() }

    // Detect if device has a real centered notch (debug override supported)
    val hasRealNotch = hasCenteredNotch

    // Calculate "v" parameter - thickness of connector based on distance
    val distance = avatarState.centerY - notchCenterY
    val maxDist = avatarSizeExpandedPx
    val c = (distance / maxDist).coerceIn(-1f, 1f)
    val connectorProgressRaw = ((dp40 - avatarState.radius) / (dp40 - dp18)).coerceIn(0f, 1f)
    val connectorProgress = easeOutCubic(connectorProgressRaw)
    val layerVisibilityBase = easeOutCubic((collapseProgress / 0.08f).coerceIn(0f, 1f))
    val layerVisibility = lerpFloat(0.35f, 1f, layerVisibilityBase)

    val baseV = ((1f - c / 1.3f) / 2f).coerceIn(0f, 0.8f)
    val near = if (avatarState.isNear) 1f else 1f - (avatarState.radius - dp32) / (dp40 - dp32)
    val vBase = if (!avatarState.isNear) {
        min(lerpFloat(0f, 0.2f, near), baseV)
    } else {
        baseV
    }.coerceIn(0.08f, 0.90f)
    val v = lerpFloat(vBase * 0.92f, (vBase + 0.14f).coerceAtMost(0.98f), connectorProgress)
        .coerceIn(0.08f, 0.98f)

    // Show connector when avatar is small enough (isDrawing) and not expanding
    // No longer requires hasRealNotch — works with black bar fallback too
    val showConnector =
            hasAvatar && expansionProgress == 0f && avatarState.showBlob &&
                    distance < maxDist * 1.6f && connectorProgress > 0.001f

    // Show metaball layer when collapsing with avatar
    val showMetaballLayer =
        hasAvatar && expansionProgress == 0f && avatarState.showBlob &&
                collapseProgress > 0.015f && layerVisibility > 0.01f
    val connectorPaintAlpha = lerpFloat(96f, 255f, connectorProgress).toInt().coerceIn(0, 255)

    // Adjusted blur radius based on device performance (factorMult)
    val adjustedBlurRadius = (ProfileMetaballConstants.BLUR_RADIUS / factorMult) *
            lerpFloat(0.9f, 1.45f, connectorProgress)

    Box(modifier = modifier
        .fillMaxSize()
        .clip(RectangleShape)
    ) {
        // LAYER 1: Metaball shapes with blur + alpha threshold (BLACK shapes only)
        if (showMetaballLayer) {
            Canvas(
                modifier = Modifier
                    .fillMaxSize()
                    .graphicsLayer {
                        alpha = layerVisibility
                    }
                    .graphicsLayer {
                        // ColorMatrixColorFilter for alpha threshold (API 31+)
                        // Replaces AGSL RuntimeShader — same gooey effect
                        val blurEffect = RenderEffect.createBlurEffect(
                            adjustedBlurRadius,
                            adjustedBlurRadius,
                            Shader.TileMode.DECAL
                        )
                        val thresholdEffect = RenderEffect.createColorFilterEffect(
                            ColorMatrixColorFilter(GPU_ALPHA_THRESHOLD_MATRIX)
                        )
                        // Chain: blur first (inner), then threshold (outer)
                        renderEffect = RenderEffect.createChainEffect(thresholdEffect, blurEffect)
                            .asComposeRenderEffect()
                    }
            ) {
                drawIntoCanvas { canvas ->
                    val nativeCanvas = canvas.nativeCanvas

                    // Draw target shape at top (notch or black bar fallback)
                    if (showConnector) {
                        blackPaint.alpha = connectorPaintAlpha
                        if (hasRealNotch && notchInfo != null) {
                            nativeCanvas.drawCircle(
                                notchCenterX,
                                notchCenterY,
                                notchRadiusPx,
                                blackPaint
                            )
                        } else {
                            // No notch fallback: full-width black bar at top
                            // Like Telegram's ProfileGooeyView when notchInfo == null
                            nativeCanvas.drawRect(0f, 0f, screenWidthPx, blackBarHeightPx, blackPaint)
                        }
                    }
                
                // Draw avatar shape - circle or rounded rect depending on state
                if (avatarState.showBlob) {
                    blackPaint.alpha = 255
                    val cx = avatarState.centerX
                    val cy = avatarState.centerY
                    val r = avatarState.radius
                    val cornerR = avatarState.cornerRadius
                    
                    // 🔥 Use rectTmp like Telegram's AndroidUtilities.rectTmp
                    // If cornerRadius is close to radius, draw circle; otherwise rounded rect
                    if (cornerR >= r * 0.95f) {
                        // Draw circle
                        nativeCanvas.drawCircle(cx, cy, r, blackPaint)
                    } else {
                        // Draw rounded rect (like Telegram when isDrawing)
                        // Reuse rectTmp to avoid allocations
                        rectTmp.set(cx - r, cy - r, cx + r, cy + r)
                        nativeCanvas.drawRoundRect(rectTmp, cornerR, cornerR, blackPaint)
                    }
                }
                
                // Draw metaball connector path
                if (showConnector && avatarState.showBlob) {
                    blackPaint.alpha = connectorPaintAlpha
                    c1.x = notchCenterX
                    c1.y = notchCenterY
                    c2.x = avatarState.centerX
                    c2.y = avatarState.centerY
                    
                    if (createMetaballPath(c1, c2, notchRadiusPx, avatarState.radius, v, metaballPath, metaballWorkspace)) {
                        nativeCanvas.drawPath(metaballPath, blackPaint)
                    }
                }
                blackPaint.alpha = 255
            }
            }
        } // END if (showMetaballLayer)
        
        // ═══════════════════════════════════════════════════════════════
        // LAYER 2: Avatar content - UNIFIED BOX with SCALE
        // 🔥 FIX: ОДИН Box для всех режимов - предотвращает мигание
        // Базовый размер ФИКСИРОВАННЫЙ (baseSizeDp), изменение через graphicsLayer.scale
        // ═══════════════════════════════════════════════════════════════
        if (avatarState.showBlob) {
            // Базовый размер аватарки (ФИКСИРОВАННЫЙ для Box)
            val baseSizeDp = ProfileMetaballConstants.AVATAR_SIZE_EXPANDED
            val baseSizePx = with(density) { baseSizeDp.toPx() }
            
            // Позиция центра (из avatarState для collapse, интерполированная для expansion)
            val avatarCenterX = avatarState.centerX
            val avatarCenterY = avatarState.centerY
            
            // ═══════════════════════════════════════════════════════════
            // 🔥 UNIFIED SCALE - БЕЗ резких переключений when
            // Всегда вычисляем scale на основе avatarState.radius
            // При expansion - дополнительно интерполируем к screenWidth
            // ═══════════════════════════════════════════════════════════
            
            // Базовый scale из avatarState.radius (работает для collapse И normal)
            val baseScale = (avatarState.radius * 2f) / baseSizePx
            
            // При expansion: дополнительно масштабируем к screenWidth
            val targetExpansionScale = screenWidthPx / baseSizePx
            val uniformScale = if (expansionProgress > 0f) {
                // Плавный переход от baseScale к targetExpansionScale
                lerpFloat(baseScale, targetExpansionScale, expansionProgress)
            } else {
                baseScale
            }.coerceAtLeast(0.01f) // Защита от деления на 0
            
            // Центр: при expansion двигается к центру header
            val targetCenterX = screenWidthPx / 2f
            val targetCenterY = headerHeightPx / 2f
            val currentCenterX = if (expansionProgress > 0f) {
                lerpFloat(avatarCenterX, targetCenterX, expansionProgress)
            } else {
                avatarCenterX
            }
            val currentCenterY = if (expansionProgress > 0f) {
                lerpFloat(avatarCenterY, targetCenterY, expansionProgress)
            } else {
                avatarCenterY
            }
            
            // Corner radius: для base size (120dp)
            // В normal/collapse: из avatarState, но пересчитанный для base size
            // При expansion: круг → квадрат
            val cornerRadiusPx: Float = if (expansionProgress > 0f) {
                // Expansion: плавно круг → квадрат
                lerpFloat(baseSizePx / 2f, 0f, expansionProgress)
            } else {
                // Normal/Collapse: используем avatarState.cornerRadius
                // Пересчитываем для base size: cornerRadius_base = cornerRadius_current / baseScale
                (avatarState.cornerRadius / baseScale).coerceAtMost(baseSizePx / 2f)
            }
            
            // Blur только при collapse близко к notch
            val applyBlur = expansionProgress == 0f && avatarState.blurRadius > 0.5f
            
            // Offset: Box имеет ФИКСИРОВАННЫЙ размер, scale применяется от центра
            val offsetX = with(density) { (currentCenterX - baseSizePx / 2f).toDp() }
            val offsetY = with(density) { (currentCenterY - baseSizePx / 2f).toDp() }
            val cornerRadiusDp = with(density) { cornerRadiusPx.toDp() }
            
            // 🔥 ЕДИНЫЙ BOX - без if/else переключения между composables
            Box(
                modifier = Modifier
                    .offset(x = offsetX, y = offsetY)
                    .width(baseSizeDp)  // ФИКСИРОВАННЫЙ
                    .height(baseSizeDp) // ФИКСИРОВАННЫЙ
                    .graphicsLayer {
                        scaleX = uniformScale
                        scaleY = uniformScale
                        alpha = avatarState.opacity
                        if (applyBlur) {
                            renderEffect = RenderEffect.createBlurEffect(
                                avatarState.blurRadius,
                                avatarState.blurRadius,
                                Shader.TileMode.DECAL
                            ).asComposeRenderEffect()
                        }
                    }
                    .clip(RoundedCornerShape(cornerRadiusDp)),
                contentAlignment = Alignment.Center,
                content = avatarContent
            )
        }
    }
}

/**
 * Compat version for older Android - simple animation without metaball effect
 * Still includes shape transition (circle → rounded rect) but no blur/gooey effects
 */
@Composable
fun ProfileMetaballOverlayCompat(
    collapseProgress: Float,
    expansionProgress: Float,
    statusBarHeight: Dp,
    headerHeight: Dp,
    hasAvatar: Boolean,
    @Suppress("UNUSED_PARAMETER") avatarColor: ComposeColor,
    modifier: Modifier = Modifier,
    avatarContent: @Composable BoxScope.() -> Unit = {},
) {
    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp.dp
    val density = LocalDensity.current
    
    val screenWidthPx = with(density) { screenWidth.toPx() }
    val statusBarHeightPx = with(density) { statusBarHeight.toPx() }
    val headerHeightPx = with(density) { headerHeight.toPx() }
    val avatarSizeExpandedPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_EXPANDED.toPx() }
    val avatarSizeMinPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_MIN.toPx() }
    
    // Fallback notch values for compat mode
    // Use black bar center as target (like Telegram's BLACK_KING_BAR)
    val blackBarHeightPx = with(density) { BLACK_BAR_HEIGHT_DP.dp.toPx() }
    val notchCenterX = screenWidthPx / 2f
    val notchCenterY = blackBarHeightPx / 2f
    val notchRadiusPx = with(density) { ProfileMetaballConstants.FALLBACK_CAMERA_SIZE.toPx() }
    
    val thresholdScale = (avatarSizeExpandedPx / with(density) { 96.dp.toPx() }).coerceIn(1f, 1.35f)
    val dp40 = with(density) { ProfileMetaballConstants.THRESHOLD_DRAWING.toPx() } * thresholdScale
    val dp34 = with(density) { ProfileMetaballConstants.THRESHOLD_SHAPE_END.toPx() } * thresholdScale
    val dp32 = with(density) { ProfileMetaballConstants.THRESHOLD_NEAR.toPx() } * thresholdScale
    val dp18 = with(density) { ProfileMetaballConstants.THRESHOLD_ALPHA_END.toPx() } * thresholdScale
    val dp22 = with(density) { ProfileMetaballConstants.ROUNDED_RECT_RADIUS.toPx() } * thresholdScale
    
    // 🔥 FIX: Убраны volatile keys (collapseProgress, expansionProgress) из remember
    // derivedStateOf автоматически отслеживает их как зависимости
    val avatarState by remember(screenWidthPx, statusBarHeightPx, headerHeightPx, notchCenterX) {
        derivedStateOf {
            computeAvatarState(
                collapseProgress = collapseProgress,
                expansionProgress = expansionProgress,
                screenWidthPx = screenWidthPx,
                statusBarHeightPx = statusBarHeightPx,
                headerHeightPx = headerHeightPx,
                avatarSizeExpandedPx = avatarSizeExpandedPx,
                avatarSizeMinPx = avatarSizeMinPx,
                hasAvatar = hasAvatar,
                notchCenterY = notchCenterY,
                notchRadiusPx = notchRadiusPx,
                dp40 = dp40,
                dp34 = dp34,
                dp32 = dp32,
                dp18 = dp18,
                dp22 = dp22,
                fullCornerRadius = avatarSizeExpandedPx / 2f
            )
        }
    }
    
    Box(modifier = modifier
        .fillMaxSize()
        .clip(RectangleShape)  // Clip to bounds - prevents avatar overflow when expanded
    ) {
        // ═══════════════════════════════════════════════════════════════
        // 🔥 FIX: ОДИН Box для всех режимов - предотвращает мигание
        // Базовый размер ФИКСИРОВАННЫЙ, изменение через graphicsLayer.scale
        // ═══════════════════════════════════════════════════════════════
        if (avatarState.showBlob) {
            val baseSizeDp = ProfileMetaballConstants.AVATAR_SIZE_EXPANDED
            val baseSizePx = with(density) { baseSizeDp.toPx() }
            val avatarCenterX = avatarState.centerX
            val avatarCenterY = avatarState.centerY
            
            // 🔥 UNIFIED SCALE - БЕЗ резких переключений when
            val baseScale = (avatarState.radius * 2f) / baseSizePx
            val targetExpansionScale = screenWidthPx / baseSizePx
            val uniformScale = if (expansionProgress > 0f) {
                lerpFloat(baseScale, targetExpansionScale, expansionProgress)
            } else {
                baseScale
            }.coerceAtLeast(0.01f)
            
            // Центр: при expansion двигается к центру header
            val targetCenterX = screenWidthPx / 2f
            val targetCenterY = headerHeightPx / 2f
            val currentCenterX = if (expansionProgress > 0f) {
                lerpFloat(avatarCenterX, targetCenterX, expansionProgress)
            } else {
                avatarCenterX
            }
            val currentCenterY = if (expansionProgress > 0f) {
                lerpFloat(avatarCenterY, targetCenterY, expansionProgress)
            } else {
                avatarCenterY
            }
            
            // Corner radius
            val cornerRadiusPx: Float = if (expansionProgress > 0f) {
                lerpFloat(baseSizePx / 2f, 0f, expansionProgress)
            } else {
                (avatarState.cornerRadius / baseScale).coerceAtMost(baseSizePx / 2f)
            }
            
            val offsetX = with(density) { (currentCenterX - baseSizePx / 2f).toDp() }
            val offsetY = with(density) { (currentCenterY - baseSizePx / 2f).toDp() }
            val cornerRadiusDp = with(density) { cornerRadiusPx.toDp() }
            
            // 🔥 ЕДИНЫЙ BOX
            Box(
                modifier = Modifier
                    .offset(x = offsetX, y = offsetY)
                    .width(baseSizeDp)
                    .height(baseSizeDp)
                    .graphicsLayer {
                        scaleX = uniformScale
                        scaleY = uniformScale
                        alpha = avatarState.opacity
                    }
                    .clip(RoundedCornerShape(cornerRadiusDp)),
                contentAlignment = Alignment.Center,
                content = avatarContent
            )
        }
    }
}

/**
 * CPU alpha threshold matrix — from Telegram's ProfileGooeyView.CPUImpl.
 * Operates on downscaled black shapes:
 * Alpha row: newAlpha = oldAlpha * 60 - 7500
 * Threshold at alpha ~125/255. RGB forced to 0 (black mask).
 */
private val CPU_ALPHA_THRESHOLD_MATRIX = floatArrayOf(
    0f, 0f, 0f, 0f, 0f,
    0f, 0f, 0f, 0f, 0f,
    0f, 0f, 0f, 0f, 0f,
    0f, 0f, 0f, 51f, -6375f  // Match GPU threshold: cutoff at alpha ~125/255
)

/**
 * CPU path for metaball effect — works on any Android version.
 * Matches Telegram's ProfileGooeyView.CPUImpl:
 * 1. Draw black shapes (target + avatar + connector) into a downscaled bitmap
 * 2. Apply stack blur on CPU
 * 3. Draw with ColorMatrixColorFilter for alpha threshold
 * 4. Composite with SRC_ATOP
 */
@Composable
fun ProfileMetaballOverlayCpu(
    collapseProgress: Float,
    expansionProgress: Float,
    statusBarHeight: Dp,
    headerHeight: Dp,
    hasAvatar: Boolean,
    @Suppress("UNUSED_PARAMETER") avatarColor: ComposeColor,
    modifier: Modifier = Modifier,
    avatarContent: @Composable BoxScope.() -> Unit = {},
) {
    val context = LocalContext.current
    val view = LocalView.current
    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp.dp
    val density = LocalDensity.current

    val screenWidthPx = with(density) { screenWidth.toPx() }
    val statusBarHeightPx = with(density) { statusBarHeight.toPx() }
    val headerHeightPx = with(density) { headerHeight.toPx() }
    val avatarSizeExpandedPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_EXPANDED.toPx() }
    val avatarSizeMinPx = with(density) { ProfileMetaballConstants.AVATAR_SIZE_MIN.toPx() }

    // Get notch info (debug override supported)
    // Try resource-based first, fallback to DisplayCutout API (for Pixel 7 Pro etc.)
    val notchInfo = remember(view) {
        NotchInfoUtils.getInfo(context) ?: NotchInfoUtils.getInfoFromCutout(view)
    }
    val hasRealNotch = remember(notchInfo, screenWidthPx) {
        isCenteredTopCutout(notchInfo, screenWidthPx)
    }
    val blackBarHeightPx = with(density) { BLACK_BAR_HEIGHT_DP.dp.toPx() }

    val notchRadiusPx = remember(notchInfo) {
        if (hasRealNotch && notchInfo != null) {
            if (notchInfo.isLikelyCircle) {
                min(notchInfo.bounds.width(), notchInfo.bounds.height()) / 2f
            } else {
                kotlin.math.max(notchInfo.bounds.width(), notchInfo.bounds.height()) / 2f
            }
        } else {
            with(density) { ProfileMetaballConstants.FALLBACK_CAMERA_SIZE.toPx() }
        }
    }
    // Always use true screen center for X target to keep droplet perfectly centered.
    val notchCenterX = screenWidthPx / 2f
    val notchCenterY = remember(notchInfo, hasRealNotch, statusBarHeightPx, blackBarHeightPx) {
        resolveSafeNotchCenterY(
            notchInfo = notchInfo,
            hasCenteredCutout = hasRealNotch,
            statusBarHeightPx = statusBarHeightPx,
            fallbackCenterY = blackBarHeightPx / 2f
        )
    }

    val thresholdScale = (avatarSizeExpandedPx / with(density) { 96.dp.toPx() }).coerceIn(1f, 1.35f)
    val dp40 = with(density) { ProfileMetaballConstants.THRESHOLD_DRAWING.toPx() } * thresholdScale
    val dp34 = with(density) { ProfileMetaballConstants.THRESHOLD_SHAPE_END.toPx() } * thresholdScale
    val dp32 = with(density) { ProfileMetaballConstants.THRESHOLD_NEAR.toPx() } * thresholdScale
    val dp18 = with(density) { ProfileMetaballConstants.THRESHOLD_ALPHA_END.toPx() } * thresholdScale
    val dp22 = with(density) { ProfileMetaballConstants.ROUNDED_RECT_RADIUS.toPx() } * thresholdScale

    val avatarState by remember(screenWidthPx, statusBarHeightPx, headerHeightPx, notchCenterX, notchCenterY, notchRadiusPx) {
        derivedStateOf {
            computeAvatarState(
                collapseProgress = collapseProgress,
                expansionProgress = expansionProgress,
                screenWidthPx = screenWidthPx,
                statusBarHeightPx = statusBarHeightPx,
                headerHeightPx = headerHeightPx,
                avatarSizeExpandedPx = avatarSizeExpandedPx,
                avatarSizeMinPx = avatarSizeMinPx,
                hasAvatar = hasAvatar,
                notchCenterY = notchCenterY,
                notchRadiusPx = notchRadiusPx,
                dp40 = dp40, dp34 = dp34, dp32 = dp32, dp18 = dp18, dp22 = dp22,
                fullCornerRadius = avatarSizeExpandedPx / 2f
            )
        }
    }

    // Reusable objects
    val metaballPath = remember { android.graphics.Path() }
    val c1 = remember { Point() }
    val c2 = remember { Point() }
    val metaballWorkspace = remember { MetaballWorkspace() }
    val rectTmp = remember { RectF() }

    // Connector calculations
    val distance = avatarState.centerY - notchCenterY
    val maxDist = avatarSizeExpandedPx
    val connectorProgressRaw = ((dp40 - avatarState.radius) / (dp40 - dp18)).coerceIn(0f, 1f)
    val connectorProgress = easeOutCubic(connectorProgressRaw)
    val layerVisibilityBase = easeOutCubic((collapseProgress / 0.08f).coerceIn(0f, 1f))
    val layerVisibility = lerpFloat(0.35f, 1f, layerVisibilityBase)
    val cParam = (distance / maxDist).coerceIn(-1f, 1f)
    val baseV = ((1f - cParam / 1.3f) / 2f).coerceIn(0f, 0.8f)
    val near = if (avatarState.isNear) 1f else 1f - (avatarState.radius - dp32) / (dp40 - dp32)
    val vBase = if (!avatarState.isNear) min(lerpFloat(0f, 0.2f, near), baseV) else baseV
    val vBaseClamped = vBase.coerceIn(0.08f, 0.90f)
    val v = lerpFloat(vBaseClamped * 0.92f, (vBaseClamped + 0.14f).coerceAtMost(0.98f), connectorProgress)
        .coerceIn(0.08f, 0.98f)

    val showConnector =
        hasAvatar && expansionProgress == 0f && avatarState.showBlob &&
                distance < maxDist * 1.6f && connectorProgress > 0.001f
    val showMetaballLayer =
        hasAvatar && expansionProgress == 0f && avatarState.showBlob &&
                collapseProgress > 0.015f && layerVisibility > 0.01f
    val connectorPaintAlpha = lerpFloat(96f, 255f, connectorProgress).toInt().coerceIn(0, 255)

    // CPU-specific: downscaled bitmap for blur
    // Higher internal resolution for cleaner, denser metaball edges.
    val scaleConst = 2.5f
    val optimizedW = min(with(density) { 120.dp.toPx() }.toInt(), screenWidthPx.toInt())
    val optimizedH = min(with(density) { 220.dp.toPx() }.toInt(), headerHeightPx.toInt() + blackBarHeightPx.toInt())
    val bitmapW = (optimizedW / scaleConst).toInt().coerceAtLeast(1)
    val bitmapH = (optimizedH / scaleConst).toInt().coerceAtLeast(1)

    val offscreenBitmap = remember(bitmapW, bitmapH) {
        android.graphics.Bitmap.createBitmap(bitmapW, bitmapH, android.graphics.Bitmap.Config.ARGB_8888)
    }
    val offscreenCanvas = remember(offscreenBitmap) {
        android.graphics.Canvas(offscreenBitmap)
    }

    // Reusable paints
    val blackPaint = remember {
        android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG).apply {
            color = android.graphics.Color.BLACK
            style = android.graphics.Paint.Style.FILL
        }
    }
    val filterPaint = remember {
        android.graphics.Paint(android.graphics.Paint.FILTER_BITMAP_FLAG or android.graphics.Paint.ANTI_ALIAS_FLAG).apply {
            isFilterBitmap = true
            colorFilter = ColorMatrixColorFilter(CPU_ALPHA_THRESHOLD_MATRIX)
        }
    }

    // Cleanup bitmap on dispose
    DisposableEffect(offscreenBitmap) {
        onDispose {
            if (!offscreenBitmap.isRecycled) {
                offscreenBitmap.recycle()
            }
        }
    }

    Box(modifier = modifier
        .fillMaxSize()
        .clip(RectangleShape)
    ) {
        // LAYER 1: CPU-rendered metaball shapes
        if (showMetaballLayer) {
            Canvas(
                modifier = Modifier
                    .fillMaxSize()
                    .graphicsLayer {
                        alpha = layerVisibility
                    }
            ) {
                drawIntoCanvas { canvas ->
                    val nativeCanvas = canvas.nativeCanvas
                    val optimizedOffsetX = (screenWidthPx - optimizedW) / 2f

                    // Clear offscreen bitmap
                    offscreenBitmap.eraseColor(0)

                    // Draw shapes into downscaled bitmap
                    offscreenCanvas.save()
                    offscreenCanvas.scale(
                        offscreenBitmap.width.toFloat() / optimizedW,
                        offscreenBitmap.height.toFloat() / optimizedH
                    )
                    offscreenCanvas.translate(-optimizedOffsetX, 0f)

                    // Draw target (notch or black bar)
                    if (showConnector) {
                        blackPaint.alpha = connectorPaintAlpha
                        if (hasRealNotch && notchInfo != null) {
                            offscreenCanvas.drawCircle(
                                notchCenterX,
                                notchCenterY,
                                notchRadiusPx,
                                blackPaint
                            )
                        } else {
                            // No notch: draw black bar
                            offscreenCanvas.drawRect(0f, 0f, screenWidthPx, blackBarHeightPx, blackPaint)
                        }
                    }

                    // Draw avatar shape
                    if (avatarState.showBlob) {
                        blackPaint.alpha = 255
                        val cx = avatarState.centerX
                        val cy = avatarState.centerY
                        val r = avatarState.radius
                        val cornerR = avatarState.cornerRadius

                        if (cornerR >= r * 0.95f) {
                            offscreenCanvas.drawCircle(cx, cy, r, blackPaint)
                        } else {
                            rectTmp.set(cx - r, cy - r, cx + r, cy + r)
                            offscreenCanvas.drawRoundRect(rectTmp, cornerR, cornerR, blackPaint)
                        }
                    }

                    // Draw metaball connector
                    if (showConnector && avatarState.showBlob) {
                        blackPaint.alpha = connectorPaintAlpha
                        c1.x = notchCenterX
                        c1.y = notchCenterY
                        c2.x = avatarState.centerX
                        c2.y = avatarState.centerY

                        if (createMetaballPath(c1, c2, notchRadiusPx, avatarState.radius, v, metaballPath, metaballWorkspace)) {
                            offscreenCanvas.drawPath(metaballPath, blackPaint)
                        }
                    }
                    blackPaint.alpha = 255

                    offscreenCanvas.restore()

                    // Apply stack blur on CPU
                    val blurRadius = ((ProfileMetaballConstants.BLUR_RADIUS * 2f / scaleConst) *
                            lerpFloat(0.9f, 1.45f, connectorProgress)).toInt().coerceAtLeast(1)
                    stackBlurBitmapInPlace(offscreenBitmap, blurRadius)

                    // Draw blurred bitmap with color matrix filter (alpha threshold)
                    nativeCanvas.save()
                    nativeCanvas.translate(optimizedOffsetX, 0f)
                    nativeCanvas.scale(
                        optimizedW.toFloat() / offscreenBitmap.width,
                        optimizedH.toFloat() / offscreenBitmap.height
                    )
                    nativeCanvas.drawBitmap(offscreenBitmap, 0f, 0f, filterPaint)
                    nativeCanvas.restore()
                }
            }
        }

        // LAYER 2: Avatar content (same as GPU path)
        if (avatarState.showBlob) {
            val baseSizeDp = ProfileMetaballConstants.AVATAR_SIZE_EXPANDED
            val baseSizePx = with(density) { baseSizeDp.toPx() }

            val baseScale = (avatarState.radius * 2f) / baseSizePx
            val targetExpansionScale = screenWidthPx / baseSizePx
            val uniformScale = if (expansionProgress > 0f) {
                lerpFloat(baseScale, targetExpansionScale, expansionProgress)
            } else {
                baseScale
            }.coerceAtLeast(0.01f)

            val avatarCenterX = avatarState.centerX
            val avatarCenterY = avatarState.centerY
            val targetCenterX = screenWidthPx / 2f
            val targetCenterY = headerHeightPx / 2f
            val currentCenterX = if (expansionProgress > 0f) lerpFloat(avatarCenterX, targetCenterX, expansionProgress) else avatarCenterX
            val currentCenterY = if (expansionProgress > 0f) lerpFloat(avatarCenterY, targetCenterY, expansionProgress) else avatarCenterY

            val cornerRadiusPx: Float = if (expansionProgress > 0f) {
                lerpFloat(baseSizePx / 2f, 0f, expansionProgress)
            } else {
                (avatarState.cornerRadius / baseScale).coerceAtMost(baseSizePx / 2f)
            }

            val offsetX = with(density) { (currentCenterX - baseSizePx / 2f).toDp() }
            val offsetY = with(density) { (currentCenterY - baseSizePx / 2f).toDp() }
            val cornerRadiusDp = with(density) { cornerRadiusPx.toDp() }

            Box(
                modifier = Modifier
                    .offset(x = offsetX, y = offsetY)
                    .width(baseSizeDp)
                    .height(baseSizeDp)
                    .graphicsLayer {
                        scaleX = uniformScale
                        scaleY = uniformScale
                        alpha = avatarState.opacity
                    }
                    .clip(RoundedCornerShape(cornerRadiusDp)),
                contentAlignment = Alignment.Center,
                content = avatarContent
            )
        }
    }
}

/**
 * Auto-selecting wrapper — 3-tier architecture matching Telegram's ProfileGooeyView:
 * 1. GPU path (Android 12+, average+ performance): RenderEffect blur + ColorMatrixColorFilter
 * 2. CPU path (any Android, high performance): Bitmap blur + ColorMatrixColorFilter
 * 3. Noop path (low-end devices): Simple scale/opacity animation
 */
@Composable
fun ProfileMetaballEffect(
    collapseProgress: Float,
    expansionProgress: Float,
    statusBarHeight: Dp,
    headerHeight: Dp,
    hasAvatar: Boolean,
    avatarColor: ComposeColor,
    modifier: Modifier = Modifier,
    avatarContent: @Composable BoxScope.() -> Unit = {},
) {
    val context = LocalContext.current
    val performanceClass = remember { DevicePerformanceClass.get(context) }

    // Resolve actual mode
    val useGpu = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
    val useCpu = !useGpu

    when {
        useGpu -> {
            val factorMult = when (performanceClass) {
                PerformanceClass.HIGH -> 1f
                PerformanceClass.AVERAGE -> 1.2f
                PerformanceClass.LOW -> 1.35f
            }
            ProfileMetaballOverlay(
                collapseProgress = collapseProgress,
                expansionProgress = expansionProgress,
                statusBarHeight = statusBarHeight,
                headerHeight = headerHeight,
                hasAvatar = hasAvatar,
                avatarColor = avatarColor,
                factorMult = factorMult,
                modifier = modifier,
                avatarContent = avatarContent
            )
        }
        useCpu -> {
            ProfileMetaballOverlayCpu(
                collapseProgress = collapseProgress,
                expansionProgress = expansionProgress,
                statusBarHeight = statusBarHeight,
                headerHeight = headerHeight,
                hasAvatar = hasAvatar,
                avatarColor = avatarColor,
                modifier = modifier,
                avatarContent = avatarContent
            )
        }
        else -> {
            ProfileMetaballOverlayCompat(
                collapseProgress = collapseProgress,
                expansionProgress = expansionProgress,
                statusBarHeight = statusBarHeight,
                headerHeight = headerHeight,
                hasAvatar = hasAvatar,
                avatarColor = avatarColor,
                modifier = modifier,
                avatarContent = avatarContent
            )
        }
    }
}
