package com.rosetta.messenger.ui.components

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Shader
import android.util.LruCache
import android.os.Build
import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.foundation.Image
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.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import com.rosetta.messenger.R
import com.rosetta.messenger.data.MessageRepository
import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.utils.AvatarFileManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.math.abs
import java.util.Collections

private object BlurredAvatarMemoryCache {
    private val cache = object : LruCache<String, Bitmap>(18 * 1024) {
        override fun sizeOf(key: String, value: Bitmap): Int = (value.byteCount / 1024).coerceAtLeast(1)
    }
    private val latestByPublicKey = object : LruCache<String, Bitmap>(12 * 1024) {
        override fun sizeOf(key: String, value: Bitmap): Int = (value.byteCount / 1024).coerceAtLeast(1)
    }

    fun get(key: String): Bitmap? = cache.get(key)
    fun getLatest(publicKey: String): Bitmap? = latestByPublicKey.get(publicKey)
    fun put(key: String, bitmap: Bitmap) {
        if (bitmap.isRecycled) return
        cache.put(key, bitmap)
        val publicKey = key.substringBeforeLast(':', "")
        if (publicKey.isNotEmpty()) {
            latestByPublicKey.put(publicKey, bitmap)
        }
    }
}

private val blurPrewarmInFlight: MutableSet<String> = Collections.synchronizedSet(mutableSetOf())

internal suspend fun prewarmBlurredAvatarFromBitmap(
    context: Context,
    publicKey: String,
    avatarTimestamp: Long,
    sourceBitmap: Bitmap,
    blurRadius: Float = 20f
) {
    if (sourceBitmap.isRecycled || publicKey.isBlank()) return
    val cacheKey = "$publicKey:$avatarTimestamp"
    if (BlurredAvatarMemoryCache.get(cacheKey) != null) return
    if (!blurPrewarmInFlight.add(cacheKey)) return

    try {
        val blurred = withContext(Dispatchers.Default) {
            gaussianBlur(context, sourceBitmap, radius = blurRadius, passes = 2)
        }
        BlurredAvatarMemoryCache.put(cacheKey, blurred)
    } finally {
        blurPrewarmInFlight.remove(cacheKey)
    }
}

/**
 * Компонент для отображения размытого фона аватарки
 * Используется в профиле и сайдбаре
 * ВАЖНО: Должен вызываться внутри BoxScope чтобы matchParentSize() работал
 */
@Composable
fun BoxScope.BlurredAvatarBackground(
    publicKey: String,
    avatarRepository: AvatarRepository?,
    fallbackColor: Color,
    blurRadius: Float = 25f,
    alpha: Float = 0.9f,
    overlayColors: List<Color>? = null,
    isDarkTheme: Boolean = true
) {
    val context = LocalContext.current
    val hasColorOverlay = overlayColors != null && overlayColors.isNotEmpty()

    val avatars by avatarRepository?.getAvatars(publicKey, allDecode = false)?.collectAsState()
        ?: remember { mutableStateOf(emptyList()) }

    val avatarKey = remember(avatars) {
        avatars.firstOrNull()?.timestamp ?: 0L
    }
    val blurCacheKey = remember(publicKey, avatarKey) { "$publicKey:$avatarKey" }

    var originalBitmap by remember(publicKey) { mutableStateOf<Bitmap?>(null) }
    var blurredBitmap by remember(publicKey) {
        mutableStateOf(BlurredAvatarMemoryCache.getLatest(publicKey))
    }

    LaunchedEffect(publicKey, avatarKey, blurCacheKey, hasColorOverlay) {
        // For custom color/gradient background we intentionally disable avatar blur layer.
        if (hasColorOverlay) {
            return@LaunchedEffect
        }

        BlurredAvatarMemoryCache.get(blurCacheKey)?.let { cached ->
            blurredBitmap = cached
            return@LaunchedEffect
        }

        val currentAvatars = avatars
        val newOriginal = withContext(Dispatchers.IO) {
            // Keep system account blur source identical to the visible avatar drawable.
            // This prevents offset/parallax mismatches when server avatar payload differs.
            if (MessageRepository.isSystemAccount(publicKey)) {
                loadSystemAvatarBitmap(context, publicKey)
            } else if (currentAvatars.isNotEmpty()) {
                AvatarFileManager.base64ToBitmap(currentAvatars.first().base64Data)
            } else {
                loadSystemAvatarBitmap(context, publicKey)
            }
        }
        if (newOriginal != null) {
            originalBitmap = newOriginal
            val blurred = withContext(Dispatchers.Default) {
                gaussianBlur(context, newOriginal, radius = blurRadius, passes = 2)
            }
            blurredBitmap = blurred
            BlurredAvatarMemoryCache.put(blurCacheKey, blurred)
        } else if (blurredBitmap == null) {
            originalBitmap = null
        }
    }

    // ═══════════════════════════════════════════════════════════
    // Режим с overlay-цветом — blur + пастельный overlay
    // ═══════════════════════════════════════════════════════════
    if (hasColorOverlay) {
        val overlayPalette = overlayColors.orEmpty()
        // LAYER 1: Pure color/gradient background (no avatar blur behind avatar).
        val overlayMod = if (overlayPalette.size == 1) {
            Modifier.matchParentSize()
                .background(overlayPalette[0])
        } else {
            Modifier.matchParentSize()
                .background(
                    Brush.linearGradient(
                        colors = overlayPalette
                    )
                )
        }
        Box(modifier = overlayMod)

        // LAYER 2: Нижний градиент для читаемости текста
        Box(
            modifier = Modifier
                .matchParentSize()
                .background(
                    Brush.verticalGradient(
                        colors = listOf(
                            Color.Transparent,
                            Color.Transparent,
                            Color.Black.copy(alpha = 0.2f)
                        )
                    )
                )
        )
        return
    }

    // ═══════════════════════════════════════════════════════════
    // Стандартный режим (нет overlay-цвета) — blur аватарки
    // Telegram-style: яркий видный блюр + мягкое затемнение
    // ═══════════════════════════════════════════════════════════
    val displayBitmap = blurredBitmap ?: originalBitmap
    if (displayBitmap != null) {
        // LAYER 1: Размытая аватарка — яркая и видная
        Image(
            bitmap = displayBitmap.asImageBitmap(),
            contentDescription = null,
            modifier = Modifier.matchParentSize()
                .graphicsLayer { this.alpha = if (blurredBitmap != null) 0.9f else 0.72f },
            contentScale = ContentScale.Crop
        )
        // LAYER 2: Лёгкое тонирование цветом аватара
        Box(
            modifier = Modifier
                .matchParentSize()
                .background(fallbackColor.copy(alpha = 0.12f))
        )
        // LAYER 3: Мягкий нижний градиент для текста
        Box(
            modifier = Modifier
                .matchParentSize()
                .background(
                    Brush.verticalGradient(
                        colors = listOf(
                            Color.Transparent,
                            Color.Transparent,
                            Color.Black.copy(alpha = 0.25f)
                        )
                    )
                )
        )
    } else {
        Box(
            modifier = Modifier
                .matchParentSize()
                .background(
                    if (isDarkTheme) Color(0xFF2A2A2E) else Color(0xFFD8D8DC)
                )
        )
    }
}

private fun resolveSystemAvatarRes(publicKey: String): Int? =
    when (publicKey) {
        MessageRepository.SYSTEM_SAFE_PUBLIC_KEY -> R.drawable.safe_account
        MessageRepository.SYSTEM_UPDATES_PUBLIC_KEY -> R.drawable.updates_account
        else -> null
    }

private fun loadSystemAvatarBitmap(context: Context, publicKey: String): Bitmap? {
    val resId = resolveSystemAvatarRes(publicKey) ?: return null
    val drawable = AppCompatResources.getDrawable(context, resId) ?: return null
    val width = drawable.intrinsicWidth.takeIf { it > 0 } ?: 256
    val height = drawable.intrinsicHeight.takeIf { it > 0 } ?: 256
    val source = drawable.toBitmap(width = width, height = height, config = Bitmap.Config.ARGB_8888)
    return recenterSystemBlurSource(source)
}

private data class FocusCenter(val x: Float, val y: Float)

private fun recenterSystemBlurSource(source: Bitmap): Bitmap {
    val focusCenter = detectSystemFocusCenter(source) ?: return source
    val dx = source.width / 2f - focusCenter.x
    val dy = source.height / 2f - focusCenter.y

    if (abs(dx) < 0.5f && abs(dy) < 0.5f) return source

    val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(output)
    val shader = BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
    val matrix = Matrix().apply { setTranslate(dx, dy) }
    shader.setLocalMatrix(matrix)
    val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        this.shader = shader
        isFilterBitmap = true
        isDither = true
    }
    canvas.drawRect(0f, 0f, source.width.toFloat(), source.height.toFloat(), paint)
    return output
}

private fun detectSystemFocusCenter(source: Bitmap): FocusCenter? {
    val width = source.width
    val height = source.height
    if (width < 2 || height < 2) return null

    val cornerColors = intArrayOf(
        source.getPixel(0, 0),
        source.getPixel(width - 1, 0),
        source.getPixel(0, height - 1),
        source.getPixel(width - 1, height - 1)
    )
    val bgR = cornerColors.map { android.graphics.Color.red(it) }.average().toFloat()
    val bgG = cornerColors.map { android.graphics.Color.green(it) }.average().toFloat()
    val bgB = cornerColors.map { android.graphics.Color.blue(it) }.average().toFloat()

    val step = (minOf(width, height) / 140).coerceAtLeast(1)
    val threshold = 54f
    var weightedX = 0f
    var weightedY = 0f
    var totalWeight = 0f

    var y = 0
    while (y < height) {
        var x = 0
        while (x < width) {
            val pixel = source.getPixel(x, y)
            val dr = abs(android.graphics.Color.red(pixel) - bgR)
            val dg = abs(android.graphics.Color.green(pixel) - bgG)
            val db = abs(android.graphics.Color.blue(pixel) - bgB)
            val weight = (dr + dg + db) - threshold
            if (weight > 0f) {
                weightedX += x * weight
                weightedY += y * weight
                totalWeight += weight
            }
            x += step
        }
        y += step
    }

    if (totalWeight <= 0f) return null
    return FocusCenter(
        x = weightedX / totalWeight,
        y = weightedY / totalWeight
    )
}

/**
 * Gaussian blur via RenderScript.
 * Pads the image with mirrored edges before blurring to eliminate edge banding artifacts,
 * then crops back to original size.
 */
@Suppress("deprecation")
private fun gaussianBlur(context: Context, source: Bitmap, radius: Float = 20f, passes: Int = 2): Bitmap {
    // Scale down for performance
    val w = (source.width / 3).coerceAtLeast(32)
    val h = (source.height / 3).coerceAtLeast(32)
    val scaled = Bitmap.createScaledBitmap(source, w, h, true)

    // Add padding around the image to prevent edge clamping artifacts
    val pad = (radius * passes).toInt().coerceAtLeast(8)
    val paddedW = w + pad * 2
    val paddedH = h + pad * 2
    val padded = Bitmap.createBitmap(paddedW, paddedH, Bitmap.Config.ARGB_8888)
    val canvas = android.graphics.Canvas(padded)

    // Draw center
    canvas.drawBitmap(scaled, pad.toFloat(), pad.toFloat(), null)

    // Mirror edges: left, right, top, bottom strips
    for (x in 0 until pad) {
        for (y in 0 until h) {
            val pixel = scaled.getPixel((pad - x - 1).coerceIn(0, w - 1), y)
            padded.setPixel(x, y + pad, pixel)
            val pixel2 = scaled.getPixel((w - 1 - (x.coerceIn(0, w - 1))), y)
            padded.setPixel(paddedW - 1 - x, y + pad, pixel2)
        }
    }
    for (y in 0 until pad) {
        for (x in 0 until paddedW) {
            val srcX = (x - pad).coerceIn(0, w - 1)
            val pixel = scaled.getPixel(srcX, (pad - y - 1).coerceIn(0, h - 1))
            padded.setPixel(x, y, pixel)
            val pixel2 = scaled.getPixel(srcX, (h - 1 - y.coerceIn(0, h - 1)))
            padded.setPixel(x, paddedH - 1 - y, pixel2)
        }
    }

    var current = padded.copy(Bitmap.Config.ARGB_8888, true)

    val rs = RenderScript.create(context)
    val blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
    blur.setRadius(radius.coerceIn(1f, 25f))

    repeat(passes) {
        val input = Allocation.createFromBitmap(rs, current)
        val output = Allocation.createFromBitmap(rs, current)
        blur.setInput(input)
        blur.forEach(output)
        output.copyTo(current)
        input.destroy()
        output.destroy()
    }

    blur.destroy()
    rs.destroy()

    // Crop back to original size (remove padding)
    return Bitmap.createBitmap(current, pad, pad, w, h)
}
