package com.tt.honeyDue.util

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import androidx.exifinterface.media.ExifInterface
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import kotlin.math.roundToInt

/**
 * Android implementation of [ImageCompression].
 *
 * Pipeline:
 *   1. Decode input bytes → [Bitmap] via [BitmapFactory].
 *   2. Read EXIF orientation via [ExifInterface] (from a secondary stream,
 *      since `ExifInterface` consumes it).
 *   3. Apply orientation rotation/flip into the bitmap via [Matrix].
 *   4. If the long edge exceeds [maxEdgePx], downscale preserving aspect.
 *   5. Re-encode as JPEG at `quality * 100`.
 *
 * The output JPEG carries no EXIF orientation tag (the rotation is baked
 * into pixels), matching the iOS `UIImage.jpegData(compressionQuality:)`
 * behaviour where the output is always upright.
 */
actual object ImageCompression {

    actual suspend fun compress(
        input: ByteArray,
        maxEdgePx: Int,
        quality: Float
    ): ByteArray = withContext(Dispatchers.Default) {
        // --- decode ---------------------------------------------------------
        val decoded = BitmapFactory.decodeByteArray(input, 0, input.size)
            ?: return@withContext input  // not a decodable image — pass through

        // --- read EXIF orientation -----------------------------------------
        val orientation = try {
            ExifInterface(ByteArrayInputStream(input))
                .getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL
                )
        } catch (_: Throwable) {
            ExifInterface.ORIENTATION_NORMAL
        }

        // --- apply EXIF orientation ----------------------------------------
        val oriented = applyExifOrientation(decoded, orientation)

        // --- downscale if needed -------------------------------------------
        val scaled = downscaleIfNeeded(oriented, maxEdgePx)

        // --- encode JPEG ---------------------------------------------------
        val clampedQuality = quality.coerceIn(0f, 1f)
        val jpegQuality = (clampedQuality * 100f).roundToInt()
        val out = ByteArrayOutputStream()
        scaled.compress(Bitmap.CompressFormat.JPEG, jpegQuality, out)

        // Free intermediate bitmaps if we allocated new ones.
        if (scaled !== decoded) scaled.recycle()
        if (oriented !== decoded && oriented !== scaled) oriented.recycle()
        decoded.recycle()

        out.toByteArray()
    }

    /**
     * Apply an EXIF orientation value to a bitmap, returning a new bitmap with
     * the rotation/flip baked in. If orientation is normal/undefined, the
     * original bitmap is returned.
     */
    private fun applyExifOrientation(src: 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.postScale(-1f, 1f)
            ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1f, -1f)
            ExifInterface.ORIENTATION_TRANSPOSE -> {
                matrix.postRotate(90f); matrix.postScale(-1f, 1f)
            }
            ExifInterface.ORIENTATION_TRANSVERSE -> {
                matrix.postRotate(270f); matrix.postScale(-1f, 1f)
            }
            else -> return src  // NORMAL or UNDEFINED: nothing to do.
        }
        return Bitmap.createBitmap(src, 0, 0, src.width, src.height, matrix, true)
    }

    /**
     * Downscale [src] so its longest edge is at most [maxEdgePx], preserving
     * aspect ratio. Returns the input unchanged if it already fits.
     */
    private fun downscaleIfNeeded(src: Bitmap, maxEdgePx: Int): Bitmap {
        val longEdge = maxOf(src.width, src.height)
        if (longEdge <= maxEdgePx || maxEdgePx <= 0) return src

        val scale = maxEdgePx.toFloat() / longEdge.toFloat()
        val targetW = (src.width * scale).roundToInt().coerceAtLeast(1)
        val targetH = (src.height * scale).roundToInt().coerceAtLeast(1)
        return Bitmap.createScaledBitmap(src, targetW, targetH, /* filter = */ true)
    }
}
