package com.lemenzo.messenger.mms;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/** Bounded attachment loader with carrier-aware image downscaling/transcoding. */
public final class MmsAttachmentPreprocessor {
    public static final class Result {
        public final byte[] data;
        public final String mimeType;
        Result(byte[] data, String mimeType) {
            this.data = data;
            this.mimeType = mimeType;
        }
    }

    private MmsAttachmentPreprocessor() {}

    public static Result prepare(Context context, Uri uri, String mimeType, MmsConfig config,
            int textBytes) throws IOException {
        if (uri == null) throw new IOException("Missing attachment URI");
        String mime = mimeType == null || mimeType.isEmpty()
                ? "application/octet-stream" : mimeType;
        int budget = config.attachmentBudget(textBytes);

        if (mime.startsWith("image/")) {
            return prepareImage(context, uri, config, budget);
        }

        byte[] raw = readBounded(context, uri, budget + 1);
        if (raw.length > budget) throw new IOException("Attachment exceeds carrier MMS limit");
        return new Result(raw, mime);
    }

    private static Result prepareImage(Context context, Uri uri, MmsConfig config, int budget)
            throws IOException {
        BitmapFactory.Options bounds = new BitmapFactory.Options();
        bounds.inJustDecodeBounds = true;
        try (InputStream in = context.getContentResolver().openInputStream(uri)) {
            if (in == null) throw new IOException("Unable to open image");
            BitmapFactory.decodeStream(in, null, bounds);
        }
        if (bounds.outWidth <= 0 || bounds.outHeight <= 0) {
            byte[] raw = readBounded(context, uri, budget + 1);
            if (raw.length > budget) throw new IOException("Image exceeds carrier MMS limit");
            return new Result(raw, "image/jpeg");
        }

        int sample = 1;
        while ((bounds.outWidth / sample) > config.maxImageWidth * 2
                || (bounds.outHeight / sample) > config.maxImageHeight * 2) {
            sample *= 2;
        }

        BitmapFactory.Options decode = new BitmapFactory.Options();
        decode.inSampleSize = Math.max(1, sample);
        Bitmap bitmap;
        try (InputStream in = context.getContentResolver().openInputStream(uri)) {
            if (in == null) throw new IOException("Unable to open image");
            bitmap = BitmapFactory.decodeStream(in, null, decode);
        }
        if (bitmap == null) throw new IOException("Unable to decode image");

        Bitmap scaled = scaleInside(bitmap, config.maxImageWidth, config.maxImageHeight);
        if (scaled != bitmap) bitmap.recycle();

        try {
            for (int quality = 90; quality >= 45; quality -= 5) {
                byte[] encoded = encodeJpeg(scaled, quality);
                if (encoded.length <= budget) return new Result(encoded, "image/jpeg");
            }

            Bitmap current = scaled;
            for (int pass = 0; pass < 4; pass++) {
                int w = Math.max(320, (int) (current.getWidth() * 0.80f));
                int h = Math.max(320, (int) (current.getHeight() * 0.80f));
                if (w == current.getWidth() && h == current.getHeight()) break;
                Bitmap smaller = Bitmap.createScaledBitmap(current, w, h, true);
                if (current != scaled) current.recycle();
                current = smaller;
                byte[] encoded = encodeJpeg(current, 72);
                if (encoded.length <= budget) {
                    if (current != scaled) current.recycle();
                    return new Result(encoded, "image/jpeg");
                }
            }
            if (current != scaled) current.recycle();
            throw new IOException("Image cannot be reduced below carrier MMS limit");
        } finally {
            scaled.recycle();
        }
    }

    private static Bitmap scaleInside(Bitmap source, int maxWidth, int maxHeight) {
        int w = source.getWidth();
        int h = source.getHeight();
        float factor = Math.min(1f, Math.min(maxWidth / (float) w, maxHeight / (float) h));
        if (factor >= 0.999f) return source;
        return Bitmap.createScaledBitmap(source, Math.max(1, Math.round(w * factor)),
                Math.max(1, Math.round(h * factor)), true);
    }

    private static byte[] encodeJpeg(Bitmap bitmap, int quality) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        if (!bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)) {
            throw new IOException("JPEG compression failed");
        }
        return out.toByteArray();
    }

    private static byte[] readBounded(Context context, Uri uri, int maxBytes) throws IOException {
        try (InputStream in = context.getContentResolver().openInputStream(uri)) {
            if (in == null) throw new IOException("Unable to open attachment");
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[32 * 1024];
            int total = 0;
            int n;
            while ((n = in.read(buffer)) >= 0) {
                total += n;
                if (total > maxBytes) return new byte[total];
                out.write(buffer, 0, n);
            }
            return out.toByteArray();
        }
    }
}
