package tk.zwander.lockscreenwidgets.appwidget

import android.annotation.SuppressLint
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.BlendMode
import android.os.Build
import android.os.Bundle
import android.os.ServiceManager
import android.util.SizeF
import android.util.SparseArray
import android.util.TypedValue
import android.view.View
import android.widget.RemoteViews
import androidx.annotation.ColorInt
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.core.app.PendingIntentCompat
import androidx.core.net.toUri
import androidx.core.os.BundleCompat
import androidx.core.util.forEach
import androidx.core.util.plus
import androidx.core.widget.RemoteViewsCompat.setImageViewColorFilter
import androidx.core.widget.RemoteViewsCompat.setImageViewImageTintList
import androidx.core.widget.RemoteViewsCompat.setProgressBarIndeterminateTintBlendMode
import androidx.core.widget.RemoteViewsCompat.setViewBackgroundColor
import androidx.core.widget.RemoteViewsCompat.setViewBackgroundResource
import com.android.internal.appwidget.IAppWidgetService
import com.android.internal.widget.RecyclerView
import tk.zwander.common.appwidget.RemoteViewsProxyService
import tk.zwander.common.data.WidgetStackStyle
import tk.zwander.common.host.widgetHostCompat
import tk.zwander.common.util.*
import tk.zwander.lockscreenwidgets.App
import tk.zwander.lockscreenwidgets.BuildConfig
import tk.zwander.lockscreenwidgets.R
import tk.zwander.lockscreenwidgets.activities.WidgetStackConfigure
import kotlin.math.floor
import kotlin.math.roundToInt

class WidgetStackProvider : AppWidgetProvider() {
    private val appWidgetService by lazy {
        IAppWidgetService.Stub.asInterface(
            ServiceManager.getService(Context.APPWIDGET_SERVICE),
        )
    }

    private var fromChild = false
    private var isSwap = false
    private var refresh = false
    private lateinit var intent: Intent

    override fun onReceive(context: Context, intent: Intent) {
        fromChild = intent.getBooleanExtra(FROM_CHILD, false)
        refresh = intent.getBooleanExtra(EXTRA_REFRESH, false)
        this.intent = intent

        if (intent.action == ACTION_SWAP_INDEX) {
            isSwap = true
            val autoSwap = intent.getBooleanExtra(EXTRA_AUTO_SWAP, false)
            val targetIndex = intent.getIntExtra(EXTRA_SWAP_INDEX, RecyclerView.NO_POSITION)

            val backward = intent.getBooleanExtra(EXTRA_BACKWARD, false)
            val widgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)

            widgetIds?.forEach { widgetId ->
                if (autoSwap && context.prefManager.widgetStackAutoChange[widgetId]?.first != true) {
                    return@forEach
                }

                val allStacks = context.prefManager.widgetStackWidgets
                val stackedWidgets = (allStacks[widgetId] ?: LinkedHashSet()).toList()

                val index = (context.prefManager.widgetStackIndices[widgetId] ?: 0)
                    .coerceAtMost(stackedWidgets.lastIndex)

                val newIndex = if (targetIndex != RecyclerView.NO_POSITION) {
                    targetIndex
                } else {
                    if (backward) {
                        if (index - 1 >= 0) {
                            index - 1
                        } else {
                            stackedWidgets.lastIndex
                        }
                    } else {
                        if (index + 1 <= stackedWidgets.lastIndex) {
                            index + 1
                        } else {
                            0
                        }
                    }
                }

                intent.putExtra(EXTRA_SWAP_INDEX, newIndex)
                intent.putExtra(EXTRA_PREVIOUS_INDEX, index)

                context.prefManager.widgetStackIndices = context.prefManager.widgetStackIndices.apply {
                    this[widgetId] = newIndex
                }
            }

            onReceive(context, intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE))
        }

        super.onReceive(context, intent)
    }

    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray,
    ) {
        appWidgetIds.forEach { appWidgetId ->
            val root = RemoteViews(context.packageName, R.layout.widget_stack)
            val stackedWidgets = (context.prefManager.widgetStackWidgets[appWidgetId] ?: LinkedHashSet()).toList()
            val index = (context.prefManager.widgetStackIndices[appWidgetId] ?: 0)
                .coerceAtMost(stackedWidgets.lastIndex)
                .coerceAtLeast(0)
            val widgetData = stackedWidgets.getOrNull(index)
            val widgetView =
                widgetData?.let {
                    context.widgetHostCompat.cachedRemoteViews[it.id]?.let { cached ->
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                            RemoteViews(cached)
                        } else {
                            cached
                        }
                    } ?: appWidgetService.getAppWidgetViews(context.packageName, it.id)
                }

            root.setViewVisibility(
                R.id.add_widget,
                if (widgetView == null) View.VISIBLE else View.GONE,
            )
            root.setViewVisibility(
                R.id.widget_content_wrapper,
                if (widgetView == null) View.GONE else View.VISIBLE,
            )

            // Dummy click listener to prevent default click behavior opening the main app.
            root.setOnClickPendingIntent(
                android.R.id.background,
                PendingIntentCompat.getBroadcast(
                    context,
                    Int.MAX_VALUE,
                    Intent(context, WidgetStackProvider::class.java),
                    0,
                    false,
                ),
            )

            if (widgetView != null) {
                fillInWidgetViews(
                    context = context,
                    appWidgetManager = appWidgetManager,
                    stackId = appWidgetId,
                    innerWidgetId = widgetData.id,
                    index = index,
                    rootWidgetViews = widgetView,
                    root = root,
                    stackSize = stackedWidgets.size,
                )
            } else {
                root.setOnClickPendingIntent(
                    R.id.add_widget,
                    PendingIntentCompat.getActivity(
                        context,
                        appWidgetId,
                        Intent(context, WidgetStackConfigure::class.java)
                            .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId),
                        0,
                        false,
                    ),
                )
            }

            App.instance.updateAutoChangeForStack(appWidgetId)
            App.instance.updateWidgetStackMonitor()
            appWidgetManager.updateAppWidget(appWidgetId, root)
            context.eventManager.sendEvent(Event.StackUpdateComplete(appWidgetId))
        }
    }

    @SuppressLint("RestrictedApi")
    private fun fillInWidgetViews(
        context: Context,
        appWidgetManager: AppWidgetManager,
        stackId: Int,
        innerWidgetId: Int,
        index: Int,
        rootWidgetViews: RemoteViews,
        root: RemoteViews,
        stackSize: Int,
    ) {
        val options = appWidgetManager.getAppWidgetOptions(stackId)
        val applyPadding = context.prefManager.widgetStackWidgetPadding[stackId]?.get(innerWidgetId) ?: false
        val style = context.prefManager.widgetStackStyle[stackId] ?: WidgetStackStyle()
        val iconColor = style.getIconColor(context.themedContext)
        val realSize = options?.let { options ->
            extractSizeFromOptions(options, applyPadding).also { size ->
                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.width.roundToInt())
                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.width.roundToInt())
                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.height.roundToInt())
                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.height.roundToInt())
            }
        }

        setClickListeners(
            context = context,
            root = root,
            stackId = stackId,
        )

        setBackgrounds(
            root = root,
            style = style,
        )

        setColors(
            root = root,
            iconColor = iconColor,
        )

        fillInPageRow(
            context = context,
            root = root,
            realSize = realSize,
            stackSize = stackSize,
            index = index,
            stackId = stackId,
            iconColor = iconColor,
        )

        if (options?.matches(appWidgetManager.getAppWidgetOptions(innerWidgetId)) != true) {
            appWidgetManager.updateAppWidgetOptions(innerWidgetId, options)
        }

        val viewsToApply = rootWidgetViews.getRemoteViewsToApplyCompat(
            context = context,
            size = realSize,
        )

        processActions(
            context = context,
            innerView = viewsToApply,
            innerWidgetId = innerWidgetId,
        )

        hoistCollections(
            innerView = viewsToApply,
            rootWidgetViews = rootWidgetViews,
            outerView = root,
        )

        fillInFlipper(
            index = index,
            stackSize = stackSize,
            stackId = stackId,
            root = root,
            viewsToApply = viewsToApply,
        )

        applyPadding(
            context = context,
            applyPadding = applyPadding,
            root = root,
        )

        hoistWidgetData(
            innerView = viewsToApply,
            outerView = root,
        )
    }

    private fun applyPadding(
        context: Context,
        applyPadding: Boolean,
        root: RemoteViews,
    ) {
        val dpVal = with(Density(context)) {
            8.dp.roundToPx()
        }
        root.setViewPadding(
            R.id.widget_content,
            if (applyPadding) dpVal else 0,
            if (applyPadding) dpVal else 0,
            if (applyPadding) dpVal else 0,
            0,
        )
    }

    private fun setBackgrounds(
        root: RemoteViews,
        style: WidgetStackStyle,
    ) {
        val buttonBackground = if (style.showButtonBackground) R.drawable.pill else R.drawable.pill_transparent
        root.setViewBackgroundResource(R.id.add_widget, buttonBackground)
        root.setViewBackgroundResource(R.id.start_controls_wrapper, buttonBackground)
        root.setViewBackgroundResource(R.id.end_controls_wrapper, buttonBackground)
        root.setViewBackgroundResource(R.id.stack_dot_row, buttonBackground)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            root.setViewBackgroundResource(
                android.R.id.background,
                if (style.roundedCorners) {
                    R.drawable.widget_stack_background
                } else {
                    R.drawable.widget_stack_background_square
                },
            )
        }
    }

    private fun setColors(
        root: RemoteViews,
        @ColorInt
        iconColor: Int,
    ) {
        root.setImageViewColorFilter(
            R.id.stack_configure_img,
            iconColor,
        )

        root.setViewBackgroundColor(
            R.id.divider_left,
            iconColor,
        )

        root.setImageViewColorFilter(
            R.id.stack_refresh_img,
            iconColor,
        )

        root.setImageViewColorFilter(
            R.id.stack_backward_img,
            iconColor,
        )

        root.setViewBackgroundColor(
            R.id.divider_right,
            iconColor,
        )

        root.setImageViewColorFilter(
            R.id.stack_forward_img,
            iconColor,
        )
    }

    private fun setClickListeners(
        context: Context,
        root: RemoteViews,
        stackId: Int,
    ) {
        root.setOnClickPendingIntent(
            R.id.stack_forward,
            PendingIntentCompat.getBroadcast(
                context,
                stackId,
                createSwapIntent(context, intArrayOf(stackId), false),
                0,
                false,
            )
        )
        root.setOnClickPendingIntent(
            R.id.stack_backward,
            PendingIntentCompat.getBroadcast(
                context,
                stackId + 100000,
                createSwapIntent(context, intArrayOf(stackId), true),
                0,
                false,
            ),
        )
        root.setOnClickPendingIntent(
            R.id.stack_configure,
            PendingIntentCompat.getActivity(
                context,
                stackId + 10000,
                Intent(context, WidgetStackConfigure::class.java)
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
                    .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, stackId),
                0,
                false,
            ),
        )
        root.setOnClickPendingIntent(
            R.id.stack_refresh,
            PendingIntentCompat.getBroadcast(
                context,
                stackId + 20000,
                createBaseIntent(context, intArrayOf(stackId))
                    .setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
                    .putExtra(EXTRA_REFRESH, true),
                0,
                false
            )
        )
    }

    private fun fillInFlipper(
        index: Int,
        stackSize: Int,
        stackId: Int,
        root: RemoteViews,
        viewsToApply: RemoteViews,
    ) {
        val rem = index % 3
        val prevIndex = if (index > 0) {
            index - 1
        } else {
            stackSize - 1
        }
        val prevRem = prevIndex % 3
        val realRem = if (rem == prevRem) {
            if (rem > 0) {
                rem - 1
            } else {
                2
            }
        } else {
            rem
        }

        if (!isSwap || refresh) {
            root.removeAllViews(R.id.widget_content_even)
            root.removeAllViews(R.id.widget_content_odd)
            root.removeAllViews(R.id.widget_content_third)
        } else {
            App.instance.scheduleWidgetRefresh(stackId)
        }

        when (realRem) {
            0 -> {
                root.removeAllViews(R.id.widget_content_even)
                root.addView(R.id.widget_content_even, viewsToApply)
            }

            1 -> {
                root.removeAllViews(R.id.widget_content_odd)
                root.addView(R.id.widget_content_odd, viewsToApply)
            }

            2 -> {
                root.removeAllViews(R.id.widget_content_third)
                root.addView(R.id.widget_content_third, viewsToApply)
            }
        }

        root.setDisplayedChild(R.id.widget_content, realRem)
    }

    private fun fillInPageRow(
        context: Context,
        root: RemoteViews,
        realSize: SizeF?,
        stackSize: Int,
        index: Int,
        stackId: Int,
        @ColorInt
        iconColor: Int,
    ) {
        val previousIndex = intent.getIntExtra(EXTRA_PREVIOUS_INDEX, RecyclerView.NO_POSITION)

        root.removeAllViews(R.id.stack_dot_row)

        val availableDotWidth = realSize?.let { it.width - 162 }
        val singleDotWidth = 16

        val maxDots = availableDotWidth?.let {
            floor(it / singleDotWidth).toInt()
        }
        val dotSizeDp = maxDots?.takeIf { it < stackSize }?.let {
            (floor(12 * it.toFloat() / stackSize) - 1)
                .coerceAtMost(12f)
                .takeIf { size -> size >= 9 }
        }
        val showText = maxDots != null &&
                maxDots < stackSize &&
                (dotSizeDp == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.S)

        if (!showText) {
            repeat(stackSize) {
                val dot = RemoteViews(context.packageName, R.layout.widget_stack_page_dot)
                dot.setViewVisibility(
                    R.id.page_dot_active,
                    if (index == it && previousIndex != RecyclerView.NO_POSITION) {
                        View.VISIBLE
                    } else {
                        View.INVISIBLE
                    },
                )

                dot.setProgressIndeterminateTintListCompat(
                    R.id.page_dot_active,
                    ColorStateList.valueOf(iconColor),
                )

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    dot.setProgressBarIndeterminateTintBlendMode(
                        R.id.page_dot_active,
                        BlendMode.SRC_ATOP,
                    )

                    dot.setViewLayoutWidth(R.id.page_dot_active, dotSizeDp ?: 12f, TypedValue.COMPLEX_UNIT_DIP)
                    dot.setViewLayoutHeight(R.id.page_dot_active, dotSizeDp ?: 12f, TypedValue.COMPLEX_UNIT_DIP)
                }
                dot.setViewVisibility(
                    R.id.page_dot_inactive,
                    if (it == previousIndex) {
                        View.VISIBLE
                    } else {
                        View.INVISIBLE
                    },
                )
                dot.setProgressIndeterminateTintListCompat(
                    R.id.page_dot_inactive,
                    ColorStateList.valueOf(iconColor),
                )
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    dot.setProgressBarIndeterminateTintBlendMode(
                        R.id.page_dot_inactive,
                        BlendMode.SRC_ATOP,
                    )
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    dot.setViewLayoutWidth(R.id.page_dot_inactive, dotSizeDp ?: 12f, TypedValue.COMPLEX_UNIT_DIP)
                    dot.setViewLayoutHeight(R.id.page_dot_inactive, dotSizeDp ?: 12f, TypedValue.COMPLEX_UNIT_DIP)
                }
                dot.setViewVisibility(
                    R.id.page_dot_static,
                    if (previousIndex == RecyclerView.NO_POSITION || (index != previousIndex && index != it)) {
                        View.VISIBLE
                    } else {
                        View.INVISIBLE
                    },
                )
                dot.setImageViewResource(
                    R.id.page_dot_static,
                    if (index == it) {
                        R.drawable.circle_5
                    } else {
                        R.drawable.circle_0
                    },
                )
                dot.setImageViewColorFilter(
                    R.id.page_dot_static,
                    iconColor,
                )
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    dot.setImageViewImageTintList(
                        R.id.page_dot_static,
                        ColorStateList.valueOf(iconColor),
                    )
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    dot.setViewLayoutWidth(R.id.page_dot_static, dotSizeDp ?: 12f, TypedValue.COMPLEX_UNIT_DIP)
                    dot.setViewLayoutHeight(R.id.page_dot_static, dotSizeDp ?: 12f, TypedValue.COMPLEX_UNIT_DIP)
                }
                if (index != it) {
                    dot.setOnClickPendingIntent(
                        R.id.page_dot_root,
                        PendingIntentCompat.getBroadcast(
                            context,
                            80000 + stackId + it,
                            createSwapIntent(
                                context = context,
                                ids = intArrayOf(stackId),
                                backward = false,
                                autoSwap = false,
                                swapIndex = it,
                            ),
                            0,
                            false,
                        ),
                    )
                }
                root.addView(R.id.stack_dot_row, dot)
            }
        } else {
            val textViews = RemoteViews(context.packageName, R.layout.stack_page_text)
            textViews.setTextViewText(R.id.stack_page_text, "${index + 1}/${stackSize}")

            root.addView(R.id.stack_dot_row, textViews)
        }
    }

    override fun onAppWidgetOptionsChanged(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int,
        newOptions: Bundle?,
    ) {
        onUpdate(
            context,
            appWidgetManager,
            intArrayOf(appWidgetId),
        )
    }

    override fun onDeleted(context: Context, appWidgetIds: IntArray) {
        super.onDeleted(context, appWidgetIds)

        appWidgetIds.forEach { appWidgetId ->
            context.widgetHostCompat.deleteAppWidgetId(appWidgetId)
        }
    }

    companion object {
        const val ACTION_SWAP_INDEX = "${BuildConfig.APPLICATION_ID}.intent.action.SWAP_INDEX"
        const val FROM_CHILD = "from_child"
        const val EXTRA_BACKWARD = "backward"
        const val EXTRA_AUTO_SWAP = "auto_swap"
        const val EXTRA_SWAP_INDEX = "swap_index"
        const val EXTRA_PREVIOUS_INDEX = "previous_index"
        const val EXTRA_REFRESH = "refresh"

        private val remoteViewsClass = RemoteViews::class.java

        fun update(context: Context, ids: IntArray, fromChild: Boolean = false, refresh: Boolean = false) {
            context.sendBroadcast(
                createBaseIntent(context, ids)
                    .setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
                    .putExtra(FROM_CHILD, fromChild)
                    .putExtra(EXTRA_REFRESH, refresh),
            )
        }

        fun updateOptions(context: Context, ids: IntArray, options: Bundle?) {
            context.sendBroadcast(
                createBaseIntent(context, ids)
                    .setAction(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED)
                    .putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options),
            )
        }

        fun createSwapIntent(
            context: Context,
            ids: IntArray,
            backward: Boolean,
            autoSwap: Boolean = false,
            swapIndex: Int? = null,
        ): Intent {
            return createBaseIntent(context, ids)
                .setAction(ACTION_SWAP_INDEX)
                .putExtra(EXTRA_BACKWARD, backward)
                .putExtra(EXTRA_AUTO_SWAP, autoSwap)
                .apply {
                    swapIndex?.let {
                        putExtra(EXTRA_SWAP_INDEX, it)
                    }
                }
        }

        fun extractSizeFromOptions(options: Bundle, applyPadding: Boolean): SizeF {
            val horizontalPaddingDp = if (applyPadding) 16 else 0
            val verticalPaddingDp = if (applyPadding) 8 else 0
            val bottomBarHeightDp = 36
            val optionsWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH).toFloat()
            val optionsHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT).toFloat()

            val baseSize = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                val allSizes = BundleCompat.getParcelableArrayList(
                    options,
                    AppWidgetManager.OPTION_APPWIDGET_SIZES,
                    SizeF::class.java,
                )

                (allSizes?.minOfOrNull { it.width } ?: optionsWidth) to
                        (allSizes?.maxOfOrNull { it.height } ?: optionsHeight)
            } else {
                optionsWidth to optionsHeight
            }

            return SizeF(
                (baseSize.first - horizontalPaddingDp),
                    (baseSize.second - bottomBarHeightDp - verticalPaddingDp),
            )
        }

        private fun createBaseIntent(context: Context, ids: IntArray): Intent {
            return Intent(context, WidgetStackProvider::class.java)
                .putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
                .setData("widgets://${ids.contentToString()}".toUri())
        }

        @SuppressLint("PrivateApi")
        @Suppress("UNCHECKED_CAST")
        private fun hoistCollections(
            innerView: RemoteViews,
            rootWidgetViews: RemoteViews,
            outerView: RemoteViews,
        ) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
                val collectionCacheSource = remoteViewsClass.getDeclaredField("mCollectionCache")
                    .apply { isAccessible = true }
                    .get(innerView)
                val rootCollectionCacheSource = remoteViewsClass.getDeclaredField("mCollectionCache")
                    .apply { isAccessible = true }
                    .get(rootWidgetViews)

                val collectionCacheDest = remoteViewsClass.getDeclaredField("mCollectionCache")
                    .apply { isAccessible = true }
                    .get(outerView)

                val collectionCacheClass = collectionCacheSource::class.java

                val sourceIdToUriMapping = collectionCacheClass.getDeclaredField("mIdToUriMapping")
                    .apply { isAccessible = true }
                    .get(collectionCacheSource) as SparseArray<String>
                val sourceUriToCollectionMapping = collectionCacheClass.getDeclaredField("mUriToCollectionMapping")
                    .apply { isAccessible = true }
                    .get(collectionCacheSource) as Map<String, *>

                val rootSourceIdToUriMapping = collectionCacheClass.getDeclaredField("mIdToUriMapping")
                    .apply { isAccessible = true }
                    .get(rootCollectionCacheSource) as SparseArray<String>
                val rootSourceUriToCollectionMapping = collectionCacheClass.getDeclaredField("mUriToCollectionMapping")
                    .apply { isAccessible = true }
                    .get(rootCollectionCacheSource) as Map<String, *>

                (sourceIdToUriMapping + rootSourceIdToUriMapping).forEach { intentId, uri ->
                    val items = sourceUriToCollectionMapping[uri] ?: rootSourceUriToCollectionMapping[uri]!!

                    collectionCacheClass.getDeclaredMethod(
                        "addMapping",
                        Int::class.java,
                        String::class.java,
                        items::class.java,
                    ).apply {
                        isAccessible = true
                    }.invoke(
                        collectionCacheDest,
                        intentId,
                        uri,
                        items,
                    )
                }
            }
        }

        @SuppressLint("PrivateApi")
        @Suppress("UNCHECKED_CAST")
        private fun processActions(
            context: Context,
            innerView: RemoteViews,
            innerWidgetId: Int,
        ) {
            val sourceActions = remoteViewsClass.getDeclaredField("mActions")
                .apply { isAccessible = true }
                .get(innerView) as? MutableList<Any>

            sourceActions?.forEach { action ->
                val actionClass = action::class.java

                if (actionClass.name.contains("SetRemoteViewsAdapterIntent")) {
                    val wrappedIntentField = try {
                        actionClass.getDeclaredField("mIntent")
                    } catch (_: NoSuchFieldException) {
                        actionClass.getDeclaredField("intent")
                    }.apply { isAccessible = true }
                    val wrappedIntent = wrappedIntentField.get(action) as? Intent

                    if (wrappedIntent?.data == null || wrappedIntent.data?.scheme != "widgetproxy") {
                        val newIntent = RemoteViewsProxyService.createProxyIntent(
                            context = context,
                            widgetId = innerWidgetId,
                            widgetIntent = wrappedIntent,
                        )
                        wrappedIntentField.set(action, newIntent)
                    }
                }
            }
        }

        @SuppressLint("PrivateApi")
        @Suppress("UNCHECKED_CAST")
        private fun hoistWidgetData(
            innerView: RemoteViews,
            outerView: RemoteViews,
        ) {
            val sourceActions = remoteViewsClass.getDeclaredField("mActions")
                .apply { isAccessible = true }
                .get(innerView) as? MutableList<Any>

            val collectionActions = (sourceActions?.filter { action ->
                val actionClassName = action::class.java.name
                actionClassName.contains("SetRemoteCollectionItemListAdapterAction")
                        || actionClassName.contains("SetRemoteViewsAdapterIntent")
            }.orEmpty())

            val destActionsField = remoteViewsClass.getDeclaredField("mActions")
                .apply { isAccessible = true }

            if (destActionsField.get(outerView) == null) {
                destActionsField.set(outerView, collectionActions)
            } else {
                (destActionsField.get(outerView) as? MutableList<Any>)?.addAll(collectionActions)
            }
        }
    }
}
