package chat.vojo.app;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.util.Log;

import androidx.core.content.LocusIdCompat;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;

import java.util.Collections;
import java.util.Set;

/**
 * Publish a long-lived sharing shortcut for a Matrix room so the system
 * treats per-room MessagingStyle notifications as conversations on
 * Android 11+ (API 30+).
 *
 * Without a published shortcut whose id matches the notification's
 * setShortcutId(), Android falls back to the app icon for the collapsed-
 * preview avatar regardless of Person.setIcon / Builder.setLargeIcon —
 * Person icons are only consulted by the Conversation styling layer,
 * which activates exclusively for notifications backed by a real
 * ShortcutInfoCompat marked Long Lived + the SHORTCUT_CATEGORY_CONVERSATION
 * sharing category.
 *
 * Idempotent: republishing the same shortcut id is the documented "update"
 * path; ShortcutManagerCompat handles dedup internally. Cheap to call
 * from the render hot path (~ms on warm system, indistinguishable from a
 * SharedPreferences write at our scale).
 */
final class ConversationShortcuts {

    private static final String TAG = "ConvShortcuts";

    private ConversationShortcuts() {}

    /**
     * Publish or refresh the shortcut backing a room's conversation
     * notification. No-op on API < 30 — Conversation styling is an
     * Android 11+ feature; older OS versions render the notification
     * fine without the shortcut, and the largeIcon/Person.setIcon
     * pipeline is the primary avatar source on them.
     *
     * @param ctx        Context for the shortcut manager binding.
     * @param roomId     Matrix room id, used as the shortcut id so it
     *                   matches NotificationCompat.Builder.setShortcutId.
     * @param isDirect   Whether the room is a DM; flips the shortcut
     *                   category so launchers can group DMs separately.
     * @param label      Short visible label, typically the room name (or
     *                   the peer's display name for a DM).
     * @param avatar     Optional cached avatar bitmap. Null falls through
     *                   to the app launcher icon — still publishes the
     *                   shortcut so the conversation styling activates.
     */
    /**
     * Returns the published ShortcutInfoCompat so the caller can attach
     * it directly to the notification via setShortcutInfo() — this is
     * the documented "atomic publish + bind" path that avoids the race
     * where the notification posts before the shortcut publish has
     * settled and Android sees an orphan shortcut id. Null on API < 30,
     * null on failure (notification still posts cleanly).
     */
    static ShortcutInfoCompat publishForRoom(
        Context ctx,
        String roomId,
        boolean isDirect,
        String label,
        Bitmap avatar
    ) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
            return null;
        }
        if (roomId == null || roomId.isEmpty()) return null;
        try {
            // Conversation shortcut icon MUST be adaptive — official docs:
            // "To avoid unintentional clipping of your shortcut avatar,
            // provide an AdaptiveIconDrawable for the shortcut's icon."
            // Without this, Android silently falls back to the app's
            // launcher icon for the collapsed-shade conversation avatar
            // slot, even though shortcut publish + bind succeed.
            // Resource icons (mipmap.ic_launcher) already ship with
            // adaptive layers in the manifest; bitmap avatars need padding
            // so the safe zone doesn't crop them.
            IconCompat icon;
            if (avatar != null) {
                Bitmap padded = AvatarLoader.toAdaptivePaddedBitmap(avatar);
                icon = IconCompat.createWithAdaptiveBitmap(padded);
            } else {
                icon = IconCompat.createWithResource(ctx, R.mipmap.ic_launcher);
            }

            // Intent the shortcut launches when tapped from the launcher
            // long-press menu or share sheet — opens MainActivity and
            // delivers the same `room_id` extra the notification tap
            // path uses, so the existing pushNotificationActionPerformed
            // listener navigates correctly.
            Intent launchIntent = new Intent(ctx, MainActivity.class)
                .setAction(Intent.ACTION_VIEW)
                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP)
                .putExtra("room_id", roomId)
                // Capacitor PushNotificationsPlugin gates its action
                // delivery on bundle.containsKey("google.message_id"); we
                // attach an empty value so a launcher-initiated open
                // takes the same path as a push-tap.
                .putExtra("google.message_id", "");

            // Constant value of androidx.core's
            // ShortcutInfoCompat.SHORTCUT_CATEGORY_CONVERSATION. Hardcoded
            // verbatim because older androidx.core in our dependency
            // graph doesn't export the constant; the string itself is
            // platform-stable per the Android shortcut category contract.
            Set<String> categories =
                Collections.singleton("android.shortcut.conversation");

            ShortcutInfoCompat.Builder b = new ShortcutInfoCompat.Builder(ctx, roomId)
                .setShortLabel(label != null && !label.isEmpty() ? label : "Vojo")
                .setLongLabel(label != null && !label.isEmpty() ? label : "Vojo")
                .setIntent(launchIntent)
                .setIcon(icon)
                .setLongLived(true)
                .setCategories(categories)
                // LocusId mirrors the shortcut id; the OS uses it to
                // attribute the notification to a specific conversation
                // for digital-wellbeing dashboards and bubble grouping.
                .setLocusId(new LocusIdCompat(roomId))
                // Marks isDirect so launchers / share sheet can present
                // person-style affordances on DMs.
                .setIsConversation();
            // setPerson is only needed for one-on-one conversations to
            // unlock direct-share suggestions, but for a DM we also want
            // it to anchor the shortcut on the peer's identity. Skipped
            // for groups (single Person doesn't represent the room).
            if (isDirect) {
                b.setPerson(new androidx.core.app.Person.Builder()
                    // setKey must match the Person.key used in the
                    // MessagingStyle so Android's conversation
                    // attribution matches the shortcut to the
                    // notification on the same identity.
                    .setKey(roomId)
                    .setName(label != null ? label : "")
                    .setIcon(icon)
                    .build());
            }

            ShortcutInfoCompat shortcut = b.build();
            boolean ok = ShortcutManagerCompat.pushDynamicShortcut(ctx, shortcut);
            Log.i(TAG, "publish room=" + roomId + " label=" + label
                + " hasAvatar=" + (avatar != null) + " ok=" + ok);
            return shortcut;
        } catch (Throwable t) {
            // Shortcut publish is best-effort UX — a failure must not
            // sink the notification. Worst case: collapsed preview
            // falls back to app icon (same as before the shortcut path
            // existed at all).
            Log.w(TAG, "publish failed room=" + roomId, t);
            return null;
        }
    }
}
