package com.rosetta.messenger.utils

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.graphics.Matrix
import android.net.Uri
import android.os.Build
import android.util.Base64
import androidx.exifinterface.media.ExifInterface
import com.rosetta.messenger.network.ProtocolRuntimeAccess
import com.vanniktech.blurhash.BlurHash
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream

private const val TAG = "MediaUtils"

/**
 * Утилиты для работы с медиафайлами
 * Конвертация изображений в Base64, генерация blurhash
 */
object MediaUtils {
    
    // Максимальный размер изображения для отправки (сжимаем большие изображения)
    private const val MAX_IMAGE_SIZE = 1920
    private const val IMAGE_QUALITY = 85
    
    // Максимальный размер файла в МБ
    // Android ограничение: файл + base64 + шифрование = ~3x памяти
    // 20 МБ файл = ~60 МБ RAM, безопасно для большинства устройств
    const val MAX_FILE_SIZE_MB = 20

    private fun logImage(message: String) {
        ProtocolRuntimeAccess.get().addLog("🧪 IMG-UTIL | $message")
    }
    
    /**
     * Конвертировать изображение из Uri в Base64 PNG
     * Автоматически сжимает большие изображения и учитывает EXIF ориентацию
     */
    suspend fun uriToBase64Image(context: Context, uri: Uri): String? = withContext(Dispatchers.IO) {
        try {
            val uriInfo = "${uri.scheme ?: "unknown"}:${uri.lastPathSegment ?: "unknown"}"
            logImage("encode start: $uriInfo")

            // Читаем EXIF ориентацию
            val orientation = getExifOrientation(context, uri)
            logImage("orientation=$orientation")

            val boundsOptions =
                    BitmapFactory.Options().apply { inJustDecodeBounds = true }
            val boundsStream = context.contentResolver.openInputStream(uri)
            if (boundsStream == null) {
                logImage("bounds stream open failed")
                return@withContext null
            }
            boundsStream.use { inputStream ->
                // decodeStream returns null when inJustDecodeBounds=true; we only need outWidth/outHeight.
                BitmapFactory.decodeStream(inputStream, null, boundsOptions)
            }

            if (boundsOptions.outWidth <= 0 || boundsOptions.outHeight <= 0) {
                logImage("bounds decode failed, trying direct decode fallback")
            }

            val sourceWidth = boundsOptions.outWidth.coerceAtLeast(1)
            val sourceHeight = boundsOptions.outHeight.coerceAtLeast(1)
            val initialSample =
                    calculateInSampleSize(
                            sourceWidth,
                            sourceHeight,
                            MAX_IMAGE_SIZE * 2
                    )

            var bitmap: Bitmap? = null
            var sample = initialSample.coerceAtLeast(1)

            repeat(2) { attempt ->
                if (bitmap != null) return@repeat
                val decodeOptions =
                        BitmapFactory.Options().apply {
                            inSampleSize = sample
                            inPreferredConfig = Bitmap.Config.ARGB_8888
                        }

                bitmap =
                        context.contentResolver.openInputStream(uri)?.use { inputStream ->
                            BitmapFactory.decodeStream(inputStream, null, decodeOptions)
                        }

                if (bitmap == null) {
                    bitmap = context.contentResolver.openFileDescriptor(uri, "r")?.use { pfd ->
                        BitmapFactory.decodeFileDescriptor(pfd.fileDescriptor, null, decodeOptions)
                    }
                }

                if (bitmap == null) {
                    logImage("decode attempt ${attempt + 1} failed (sample=$sample)")
                    sample *= 2
                }
            }

            if (bitmap == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                bitmap = decodeWithImageDecoder(context, uri)
                if (bitmap != null) {
                    logImage("decoded via ImageDecoder fallback")
                }
            }

            if (bitmap == null) {
                logImage("decode failed after all fallbacks")
                return@withContext null
            }

            val decodedBitmap = bitmap
                    ?: run {
                        logImage("decode failed: bitmap is null after fallbacks")
                        return@withContext null
                    }

            // Применяем EXIF ориентацию (поворот/отражение)
            val orientedBitmap = applyExifOrientation(decodedBitmap, orientation)
            
            // Масштабируем если слишком большое
            val scaledBitmap = scaleDownBitmap(orientedBitmap, MAX_IMAGE_SIZE)
            if (scaledBitmap != orientedBitmap) {
                orientedBitmap.recycle()
            }
            
            
            // Конвертируем в PNG Base64
            val outputStream = ByteArrayOutputStream()
            val compressed = scaledBitmap.compress(Bitmap.CompressFormat.PNG, IMAGE_QUALITY, outputStream)
            if (!compressed) {
                logImage("bitmap compress failed")
                scaledBitmap.recycle()
                return@withContext null
            }
            val bytes = outputStream.toByteArray()
            
            val base64 = "data:image/png;base64," + Base64.encodeToString(bytes, Base64.NO_WRAP)
            
            scaledBitmap.recycle()
            logImage("encode success: outBytes=${bytes.size}")
            
            base64
        } catch (e: Exception) {
            logImage("encode exception: ${e.javaClass.simpleName}: ${e.message ?: "unknown"}")
            null
        } catch (e: OutOfMemoryError) {
            logImage("encode OOM: ${e.message ?: "unknown"}")
            null
        }
    }

    private fun decodeWithImageDecoder(context: Context, uri: Uri): Bitmap? {
        return try {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return null
            val source = ImageDecoder.createSource(context.contentResolver, uri)
            ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
                decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
                val size = info.size
                val maxDimension = maxOf(size.width, size.height)
                if (maxDimension > MAX_IMAGE_SIZE * 2) {
                    val ratio = (MAX_IMAGE_SIZE * 2).toFloat() / maxDimension.toFloat()
                    val targetW = (size.width * ratio).toInt().coerceAtLeast(1)
                    val targetH = (size.height * ratio).toInt().coerceAtLeast(1)
                    decoder.setTargetSize(targetW, targetH)
                }
            }
        } catch (_: Exception) {
            null
        } catch (_: OutOfMemoryError) {
            null
        }
    }
    
    /**
     * Читает EXIF ориентацию из изображения
     */
    private fun getExifOrientation(context: Context, uri: Uri): Int {
        return try {
            context.contentResolver.openInputStream(uri)?.use { inputStream ->
                val exif = ExifInterface(inputStream)
                exif.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL
                )
            } ?: ExifInterface.ORIENTATION_NORMAL
        } catch (e: Exception) {
            ExifInterface.ORIENTATION_NORMAL
        }
    }
    
    /**
     * Применяет EXIF ориентацию к Bitmap (поворот/отражение)
     */
    private fun applyExifOrientation(bitmap: Bitmap, orientation: Int): Bitmap {
        val matrix = Matrix()
        
        when (orientation) {
            ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
            ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
            ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
            ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.preScale(-1f, 1f)
            ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.preScale(1f, -1f)
            ExifInterface.ORIENTATION_TRANSPOSE -> {
                matrix.postRotate(90f)
                matrix.preScale(-1f, 1f)
            }
            ExifInterface.ORIENTATION_TRANSVERSE -> {
                matrix.postRotate(270f)
                matrix.preScale(-1f, 1f)
            }
            else -> return bitmap // ORIENTATION_NORMAL или неизвестный
        }
        
        return try {
            val rotatedBitmap = Bitmap.createBitmap(
                bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
            )
            if (rotatedBitmap != bitmap) {
                bitmap.recycle()
            }
            rotatedBitmap
        } catch (e: Exception) {
            bitmap
        }
    }
    
    /**
     * Генерировать Blurhash для изображения
     */
    suspend fun generateBlurhash(context: Context, uri: Uri): String = withContext(Dispatchers.IO) {
        try {
            
            val inputStream = context.contentResolver.openInputStream(uri)
                ?: return@withContext ""
            
            // Декодируем в маленький размер для blurhash
            val options = BitmapFactory.Options().apply {
                inSampleSize = 8  // Уменьшаем в 8 раз для быстрого расчета
            }
            val bitmap = BitmapFactory.decodeStream(inputStream, null, options)
            inputStream.close()
            
            if (bitmap == null) {
                return@withContext ""
            }
            
            // Генерируем blurhash
            val blurhash = BlurHash.encode(bitmap, 4, 3)
            bitmap.recycle()
            
            blurhash ?: ""
        } catch (e: Exception) {
            ""
        }
    }
    
    /**
     * Конвертировать файл из Uri в Base64
     */
    suspend fun uriToBase64File(context: Context, uri: Uri): String? = withContext(Dispatchers.IO) {
        try {

            val inputStream = context.contentResolver.openInputStream(uri)
                ?: return@withContext null

            val bytes = inputStream.readBytes()
            inputStream.close()

            val mimeType = context.contentResolver.getType(uri) ?: "application/octet-stream"
            val base64 = "data:$mimeType;base64," + Base64.encodeToString(bytes, Base64.NO_WRAP)

            base64
        } catch (e: Exception) {
            null
        }
    }
    
    /**
     * Получить размер файла из Uri
     */
    fun getFileSize(context: Context, uri: Uri): Long {
        return try {
            context.contentResolver.openInputStream(uri)?.use { 
                it.available().toLong() 
            } ?: 0L
        } catch (e: Exception) {
            0L
        }
    }
    
    /**
     * Получить имя файла из Uri
     */
    fun getFileName(context: Context, uri: Uri): String {
        var name = "file"
        try {
            context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
                val nameIndex = cursor.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
                if (nameIndex != -1 && cursor.moveToFirst()) {
                    name = cursor.getString(nameIndex) ?: "file"
                }
            }
        } catch (e: Exception) {
        }
        return name
    }
    
    /**
     * Масштабировать bitmap до максимального размера
     */
    private fun scaleDownBitmap(bitmap: Bitmap, maxSize: Int): Bitmap {
        val width = bitmap.width
        val height = bitmap.height
        
        if (width <= maxSize && height <= maxSize) {
            return bitmap
        }
        
        val ratio = width.toFloat() / height.toFloat()
        val newWidth: Int
        val newHeight: Int
        
        if (width > height) {
            newWidth = maxSize
            newHeight = (maxSize / ratio).toInt()
        } else {
            newHeight = maxSize
            newWidth = (maxSize * ratio).toInt()
        }
        
        return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true)
    }

    private fun calculateInSampleSize(width: Int, height: Int, maxDimension: Int): Int {
        var sample = 1
        while ((width / (sample * 2)) >= maxDimension || (height / (sample * 2)) >= maxDimension) {
            sample *= 2
        }
        return sample.coerceAtLeast(1)
    }
    
    /**
     * Конвертировать Bitmap в Base64 PNG
     */
    fun bitmapToBase64(bitmap: Bitmap): String {
        val outputStream = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.PNG, IMAGE_QUALITY, outputStream)
        val bytes = outputStream.toByteArray()
        return "data:image/png;base64," + Base64.encodeToString(bytes, Base64.NO_WRAP)
    }
    
    /**
     * Генерировать Blurhash из Bitmap
     */
    fun generateBlurhashFromBitmap(bitmap: Bitmap): String {
        return try {
            // Уменьшаем для быстрого расчета
            val scaledBitmap = Bitmap.createScaledBitmap(bitmap, 32, 32, true)
            val hash = BlurHash.encode(scaledBitmap, 4, 3)
            if (scaledBitmap != bitmap) {
                scaledBitmap.recycle()
            }
            hash ?: ""
        } catch (e: Exception) {
            ""
        }
    }
    
    /**
     * Получить размеры изображения из Uri без полной загрузки в память
     * Учитывает EXIF ориентацию для правильных width/height
     */
    fun getImageDimensions(context: Context, uri: Uri): Pair<Int, Int> {
        return try {
            val options = BitmapFactory.Options().apply {
                inJustDecodeBounds = true // Не загружаем в память, только размеры
            }
            context.contentResolver.openInputStream(uri)?.use { inputStream ->
                BitmapFactory.decodeStream(inputStream, null, options)
            }
            
            var width = options.outWidth
            var height = options.outHeight
            
            // Учитываем EXIF ориентацию - если поворот 90 или 270, меняем местами width/height
            val orientation = getExifOrientation(context, uri)
            if (orientation == ExifInterface.ORIENTATION_ROTATE_90 ||
                orientation == ExifInterface.ORIENTATION_ROTATE_270 ||
                orientation == ExifInterface.ORIENTATION_TRANSPOSE ||
                orientation == ExifInterface.ORIENTATION_TRANSVERSE) {
                val temp = width
                width = height
                height = temp
            }
            
            // Учитываем масштабирование (как в uriToBase64Image)
            if (width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE) {
                val ratio = width.toFloat() / height.toFloat()
                if (width > height) {
                    width = MAX_IMAGE_SIZE
                    height = (MAX_IMAGE_SIZE / ratio).toInt()
                } else {
                    height = MAX_IMAGE_SIZE
                    width = (MAX_IMAGE_SIZE * ratio).toInt()
                }
            }
            
            Pair(width, height)
        } catch (e: Exception) {
            Pair(0, 0)
        }
    }
    
    /**
     * Получить размеры изображения из Base64 строки
     */
    fun getImageDimensionsFromBase64(base64: String): Pair<Int, Int> {
        return try {
            // Убираем data URI prefix если есть
            val base64Data = if (base64.contains(",")) {
                base64.substringAfter(",")
            } else {
                base64
            }
            
            val bytes = Base64.decode(base64Data, Base64.DEFAULT)
            val options = BitmapFactory.Options().apply {
                inJustDecodeBounds = true
            }
            BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options)
            
            Pair(options.outWidth, options.outHeight)
        } catch (e: Exception) {
            Pair(0, 0)
        }
    }
}
