package com.lemenzo.messenger.mms;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.telephony.SmsManager;
import android.text.TextUtils;
import com.google.android.mms.ContentType;
import com.google.android.mms.pdu.CharacterSets;
import com.google.android.mms.pdu.EncodedStringValue;
import com.google.android.mms.pdu.PduBody;
import com.google.android.mms.pdu.PduComposer;
import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduPart;
import com.google.android.mms.pdu.SendReq;
import com.lemenzo.messenger.SettingsActivity;
import com.lemenzo.messenger.data.PendingMessageStore;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.UUID;

public final class MmsSender {
    private MmsSender() {}

    public static boolean send(Context context, String address, String text, Uri attachment,
            int subscriptionId) {
        if (TextUtils.isEmpty(address)) return false;
        if (TextUtils.isEmpty(text) && attachment == null) return false;

        String token = UUID.randomUUID().toString();
        PendingMessageStore pendingStore = new PendingMessageStore(context);
        pendingStore.addMms(token, address, text == null ? "" : text, System.currentTimeMillis(),
                subscriptionId, attachment == null ? null : attachment.toString());

        boolean accepted = sendInternal(context, token, address, text, attachment, subscriptionId);
        if (!accepted) pendingStore.markFailed(token);
        return accepted;
    }

    /** Used by retry logic after an earlier failed attempt. */
    public static boolean retryExisting(Context context, String token, String address, String text,
            Uri attachment, int subscriptionId) {
        new PendingMessageStore(context).markSending(token);
        boolean accepted = sendInternal(context, token, address, text, attachment, subscriptionId);
        if (!accepted) new PendingMessageStore(context).markFailed(token);
        return accepted;
    }

    private static boolean sendInternal(Context context, String token, String address, String text,
            Uri attachment, int subscriptionId) {
        Uri rawUri = null;
        try {
            MmsConfig config = MmsConfig.forSubscription(context, subscriptionId);
            if (!config.mmsEnabled) return false;

            List<String> recipients = parseRecipients(address);
            if (recipients.isEmpty()) return false;
            if (recipients.size() > 1 && !config.groupMmsEnabled) return false;
            if (config.recipientLimit != Integer.MAX_VALUE && recipients.size() > config.recipientLimit) {
                return false;
            }

            SendReq req = new SendReq();
            EncodedStringValue[] to = EncodedStringValue.encodeStrings(
                    recipients.toArray(new String[0]));
            if (to == null || to.length == 0) return false;
            req.setTo(to);
            req.setDate(System.currentTimeMillis() / 1000L);

            PduBody body = new PduBody();
            int payloadSize = 0;
            byte[] textBytes = TextUtils.isEmpty(text)
                    ? new byte[0] : text.getBytes(StandardCharsets.UTF_8);

            if (textBytes.length > 0) {
                PduPart textPart = new PduPart();
                textPart.setCharset(CharacterSets.UTF_8);
                textPart.setContentType(ContentType.TEXT_PLAIN.getBytes(StandardCharsets.US_ASCII));
                textPart.setContentLocation("text_0.txt".getBytes(StandardCharsets.US_ASCII));
                textPart.setContentId("text_0".getBytes(StandardCharsets.US_ASCII));
                textPart.setData(textBytes);
                body.addPart(textPart);
                payloadSize += textBytes.length;
            }

            if (attachment != null) {
                String sourceMime = context.getContentResolver().getType(attachment);
                MmsAttachmentPreprocessor.Result prepared = MmsAttachmentPreprocessor.prepare(
                        context, attachment, sourceMime, config, textBytes.length);
                String name = displayName(context, attachment);
                if ("image/jpeg".equals(prepared.mimeType) && !name.toLowerCase().endsWith(".jpg")
                        && !name.toLowerCase().endsWith(".jpeg")) {
                    name = stripExtension(name) + ".jpg";
                }

                PduPart media = new PduPart();
                media.setContentType(prepared.mimeType.getBytes(StandardCharsets.US_ASCII));
                media.setContentLocation(name.getBytes(StandardCharsets.UTF_8));
                media.setContentId(("media_" + System.currentTimeMillis())
                        .getBytes(StandardCharsets.US_ASCII));
                media.setData(prepared.data);
                body.addPart(media);
                payloadSize += prepared.data.length;
            }

            req.setBody(body);
            req.setMessageSize(payloadSize);
            req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(StandardCharsets.US_ASCII));
            req.setExpiry(7L * 24L * 60L * 60L);
            req.setPriority(PduHeaders.PRIORITY_NORMAL);

            boolean userWantsDelivery = context.getSharedPreferences(
                    SettingsActivity.PREFS, Context.MODE_PRIVATE)
                    .getBoolean(SettingsActivity.PREF_DELIVERY_REPORTS, false);
            req.setDeliveryReport(userWantsDelivery && config.deliveryReportsEnabled
                    ? PduHeaders.VALUE_YES : PduHeaders.VALUE_NO);
            req.setReadReport(PduHeaders.VALUE_NO);

            byte[] pdu = new PduComposer(context, req).make();
            if (pdu == null || pdu.length == 0 || pdu.length > config.maxMessageSize) return false;

            rawUri = MmsFileProvider.createUri(context, "send");
            File rawFile = MmsFileProvider.fileFor(context, rawUri);
            try (FileOutputStream out = new FileOutputStream(rawFile)) {
                out.write(pdu);
                out.getFD().sync();
            }

            Intent result = new Intent(context, MmsSendResultReceiver.class)
                    .setAction(MmsSendResultReceiver.ACTION_MMS_SENT)
                    .putExtra("content_uri", rawUri.toString())
                    .putExtra("subscription_id", subscriptionId)
                    .putExtra("address", address)
                    .putExtra("token", token);

            PendingIntent pending = PendingIntent.getBroadcast(context,
                    (token + ":mms_sent").hashCode(), result,
                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

            boolean accepted = MmsTransport.send(context, subscriptionId, rawUri, null, pending);
            if (!accepted) MmsFileProvider.delete(context, rawUri);
            return accepted;
        } catch (Exception e) {
            if (rawUri != null) MmsFileProvider.delete(context, rawUri);
            return false;
        }
    }

    private static List<String> parseRecipients(String raw) {
        LinkedHashSet<String> unique = new LinkedHashSet<>();
        if (raw != null) {
            for (String part : raw.split("[,;]")) {
                String recipient = part.trim();
                if (!recipient.isEmpty()) unique.add(recipient);
            }
        }
        return new ArrayList<>(unique);
    }

    private static String displayName(Context context, Uri uri) {
        try (Cursor c = context.getContentResolver().query(uri,
                new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) {
            if (c != null && c.moveToFirst()) {
                String name = c.getString(0);
                if (name != null && !name.isEmpty()) {
                    return name.replaceAll("[^A-Za-z0-9._-]", "_");
                }
            }
        } catch (RuntimeException ignored) {}
        return "attachment.bin";
    }

    private static String stripExtension(String name) {
        int dot = name.lastIndexOf('.');
        return dot > 0 ? name.substring(0, dot) : name;
    }
}
