package com.tt.honeyDue.media

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.ByteArrayOutputStream
import java.io.InputStream

/**
 * Memory-efficient image resizer for upload preprocessing on Android.
 *
 * Why not just decode + Bitmap.createScaledBitmap? createScaledBitmap
 * decodes the full source bitmap first — a 12 MP photo materializes ~50 MB
 * in RAM regardless of how big the JPEG is. That OOMs older devices.
 *
 * BitmapFactory.Options.inSampleSize, paired with inJustDecodeBounds=true
 * for a metadata-only first pass, lets us decode at a power-of-two
 * subsample. Combined with a final scaled-down draw, peak memory is
 * roughly proportional to the *output* bitmap's pixel count — not the
 * source's.
 *
 * Quality tuning matches WhatsApp-class apps: 2048 px max edge, JPEG 85.
 */
object ImageDownsampler {

    data class Profile(
        val maxPixelEdge: Int,
        /** JPEG quality 0-100. */
        val jpegQuality: Int,
    ) {
        companion object {
            val Completion = Profile(maxPixelEdge = 2048, jpegQuality = 85)
            val DocumentImage = Profile(maxPixelEdge = 2560, jpegQuality = 90)
        }
    }

    /** Downsample raw image bytes into JPEG bytes ready for upload. */
    fun downsample(bytes: ByteArray, profile: Profile): ByteArray? {
        val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
        BitmapFactory.decodeByteArray(bytes, 0, bytes.size, bounds)
        if (bounds.outWidth <= 0 || bounds.outHeight <= 0) return null

        val sampleSize = computeSampleSize(bounds.outWidth, bounds.outHeight, profile.maxPixelEdge)
        val decodeOpts = BitmapFactory.Options().apply {
            inSampleSize = sampleSize
            // ARGB_8888 keeps quality; on memory-constrained devices we
            // could drop to RGB_565 here, but for upload prep the extra
            // ~2x peak memory isn't worth the visible quality loss.
            inPreferredConfig = Bitmap.Config.ARGB_8888
        }
        val decoded = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, decodeOpts)
            ?: return null

        // Subsample is power-of-two only; the result may still be larger
        // than maxPixelEdge by up to 2x. One more proportional scale gets
        // us to the exact target.
        val scaled = scaleProportional(decoded, profile.maxPixelEdge)

        val out = ByteArrayOutputStream(64 * 1024)
        val ok = scaled.compress(Bitmap.CompressFormat.JPEG, profile.jpegQuality, out)
        // Only recycle if scaled is a different bitmap; createScaledBitmap
        // sometimes returns the input unchanged, and recycling that would
        // double-recycle below.
        if (scaled !== decoded) decoded.recycle()
        scaled.recycle()
        return if (ok) out.toByteArray() else null
    }

    /** Same, from a stream (for content:// URIs etc.). */
    fun downsample(input: InputStream, profile: Profile): ByteArray? {
        val bytes = input.use { it.readBytes() }
        return downsample(bytes, profile)
    }

    /**
     * Pick the largest power-of-two sub-sample factor that still yields
     * an image at least as large as maxPixelEdge on both axes. Mirrors
     * the canonical Android docs example.
     */
    private fun computeSampleSize(srcW: Int, srcH: Int, maxEdge: Int): Int {
        var sample = 1
        var halfW = srcW / 2
        var halfH = srcH / 2
        while (halfW >= maxEdge && halfH >= maxEdge) {
            sample *= 2
            halfW /= 2
            halfH /= 2
        }
        return sample
    }

    private fun scaleProportional(src: Bitmap, maxEdge: Int): Bitmap {
        val w = src.width
        val h = src.height
        val longest = maxOf(w, h)
        if (longest <= maxEdge) return src
        val ratio = maxEdge.toFloat() / longest.toFloat()
        val newW = (w * ratio).toInt().coerceAtLeast(1)
        val newH = (h * ratio).toInt().coerceAtLeast(1)
        return Bitmap.createScaledBitmap(src, newW, newH, true)
    }
}
