package com.erwan.revwatch.ui

import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.app.Activity
import android.app.AlertDialog
import android.app.Dialog
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.erwan.revwatch.R
import com.erwan.revwatch.ScreenAdminReceiver
import com.erwan.revwatch.SetupActivity
import com.erwan.revwatch.model.BridgeConfig
import com.erwan.revwatch.model.CellConfig
import com.erwan.revwatch.model.DashPage
import com.erwan.revwatch.model.MetricSnapshot
import com.erwan.revwatch.prefs.AppPrefs
import com.erwan.revwatch.util.IconPackHelper
import com.erwan.revwatch.util.dp
import kotlin.math.abs

private fun createWatchDialog(ctx: Context): Dialog {
    return Dialog(ctx).apply {
        requestWindowFeature(android.view.Window.FEATURE_NO_TITLE)
        setCancelable(true)
        setCanceledOnTouchOutside(true)
    }
}

private fun configureDialogWindow(dialog: Dialog, ctx: Context) {
    dialog.window?.apply {
        setBackgroundDrawableResource(android.R.color.transparent)
        setLayout(ctx.dp(260), ViewGroup.LayoutParams.WRAP_CONTENT)
        setDimAmount(0.6f)
    }
}

class VerticalPagerAdapter(
    private val onCellTap: (pageIndex: Int, cellIndex: Int, existing: CellConfig?) -> Unit,
    private val onCellDelete: (pageIndex: Int, cellIndex: Int) -> Unit,
    private val onAddPage: () -> Unit,
    private val onDeletePage: (pageIndex: Int) -> Unit,
    private val onLayoutChange: (pageIndex: Int) -> Unit,
    private val onExitEdit: () -> Unit,
    private val onLongPress: () -> Unit,
    private val onSettingChanged: () -> Unit = {},
    private val onPickCustomIcon: (packageName: String) -> Unit = {},
    private val onWallpaperPick: () -> Unit = {},
    private val onWallpaperColor: (String) -> Unit = {},
    private val onWallpaperReset: () -> Unit = {}
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        const val PAGE_GAUGES = 0
        const val PAGE_APPS = 1
        const val PAGE_SETTINGS = 2
    }

    private var config: BridgeConfig? = null
    private var snapshot = MetricSnapshot()
    private var isEditMode = false
    /** Currently visible vertical page (0=gauges, 1=apps, 2=settings). */
    var visiblePage: Int = PAGE_GAUGES
    /** Last received snapshot, for refreshing gauges when page becomes visible. */
    val latestSnapshot: MetricSnapshot get() = snapshot

    private var gaugePageHolder: GaugePageHolder? = null
    private var appsPageHolder: AppsPageHolder? = null
    private var settingsPageHolder: SettingsPageHolder? = null

    override fun getItemCount() = 3
    override fun getItemViewType(position: Int) = position

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return when (viewType) {
            PAGE_GAUGES -> GaugePageHolder(inflater.inflate(R.layout.page_gauges, parent, false))
            PAGE_APPS -> AppsPageHolder(inflater.inflate(R.layout.page_apps, parent, false))
            else -> SettingsPageHolder(inflater.inflate(R.layout.page_settings, parent, false))
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is GaugePageHolder -> {
                gaugePageHolder = holder
                holder.setup(
                    config, snapshot, isEditMode,
                    onCellTap, onCellDelete, onAddPage, onDeletePage,
                    onLayoutChange, onExitEdit, onLongPress
                )
            }
            is AppsPageHolder -> {
                appsPageHolder = holder
                holder.setup(onPickCustomIcon)
            }
            is SettingsPageHolder -> {
                settingsPageHolder = holder
                holder.setup(config, onSettingChanged, onWallpaperPick, onWallpaperColor, onWallpaperReset)
            }
        }
    }

    fun updateConfig(newConfig: BridgeConfig) {
        config = newConfig
        gaugePageHolder?.updateConfig(newConfig, isEditMode)
        settingsPageHolder?.updateConfig(newConfig)
    }

    fun updateSnapshot(newSnapshot: MetricSnapshot) {
        snapshot = newSnapshot
        // Skip gauge rendering when gauge page isn't visible
        if (visiblePage == PAGE_GAUGES) {
            gaugePageHolder?.updateSnapshot(newSnapshot)
        }
    }

    fun setEditMode(enabled: Boolean) {
        isEditMode = enabled
        gaugePageHolder?.setEditMode(enabled, config)
    }

    fun exitAppsEditMode(): Boolean {
        return appsPageHolder?.exitEditMode() ?: false
    }

    fun refreshApps() {
        appsPageHolder?.refreshApps()
    }

    // ──────── Gauge Page ────────

    class GaugePageHolder(view: View) : RecyclerView.ViewHolder(view) {
        private val gaugeHost: View = view.findViewById(R.id.gaugeHost)
        private val gaugePager: ViewPager2 = view.findViewById(R.id.gaugePager)
        private val emptyState: View = view.findViewById(R.id.emptyState)
        private val emptyAddButton: ImageView = view.findViewById(R.id.emptyAddButton)
        private val editOverlay: View = view.findViewById(R.id.editOverlay)
        private val editToolbar: View = view.findViewById(R.id.editToolbar)
        private val layoutSelector: TextView = view.findViewById(R.id.layoutSelector)
        private val deletePageBtn: ImageView = view.findViewById(R.id.deletePageBtn)
        private val doneEditBtn: ImageView = view.findViewById(R.id.doneEditBtn)
        private lateinit var gridAdapter: GridPagerAdapter
        private var currentPageIndex = 0
        private var isSetup = false

        fun setup(
            config: BridgeConfig?,
            snapshot: MetricSnapshot,
            editMode: Boolean,
            onCellTap: (Int, Int, CellConfig?) -> Unit,
            onCellDelete: (Int, Int) -> Unit,
            onAddPage: () -> Unit,
            onDeletePage: (Int) -> Unit,
            onLayoutChange: (Int) -> Unit,
            onExitEdit: () -> Unit,
            onLongPress: () -> Unit
        ) {
            if (isSetup) return
            isSetup = true

            val cfg = config ?: return

            gridAdapter = GridPagerAdapter(onCellTap, onCellDelete, onAddPage, onLongPress)

            gaugePager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
            gaugePager.offscreenPageLimit = ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT
            gaugePager.adapter = gridAdapter

            gaugePager.setPageTransformer { page, position ->
                val d = abs(position).coerceAtMost(1f)
                page.alpha = 0.3f + (1f - d) * 0.7f
                val s = 0.88f + (1f - d) * 0.12f
                page.scaleX = s
                page.scaleY = s
            }

            gaugePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
                override fun onPageSelected(position: Int) {
                    currentPageIndex = position
                    updateLayoutSelector(config)
                }
            })

            // Empty state
            emptyAddButton.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                onAddPage()
            }

            // Edit toolbar
            layoutSelector.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                onLayoutChange(currentPageIndex)
            }
            deletePageBtn.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.REJECT)
                onDeletePage(currentPageIndex)
            }
            doneEditBtn.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                onExitEdit()
            }

            gridAdapter.updatePages(cfg.pages)
            gridAdapter.updateSnapshot(snapshot)
            updateConfig(cfg, editMode)
        }

        fun updateConfig(config: BridgeConfig?, editMode: Boolean) {
            config ?: return
            gridAdapter.updatePages(config.pages)
            gridAdapter.setEditMode(editMode)

            val hasPages = config.pages.isNotEmpty()
            val editScale = 0.72f

            if (editMode) {
                gaugeHost.visibility = View.VISIBLE
                gaugeHost.scaleX = editScale
                gaugeHost.scaleY = editScale
                emptyState.visibility = View.GONE
                editToolbar.visibility = View.VISIBLE
                editOverlay.visibility = View.VISIBLE
                editOverlay.alpha = 1f
                updateLayoutSelector(config)
            } else {
                editToolbar.visibility = View.GONE
                editOverlay.visibility = View.GONE
                gaugeHost.scaleX = 1f
                gaugeHost.scaleY = 1f
                if (hasPages) {
                    gaugeHost.visibility = View.VISIBLE
                    emptyState.visibility = View.GONE
                } else {
                    gaugeHost.visibility = View.GONE
                    emptyState.visibility = View.VISIBLE
                }
            }
        }

        fun updateSnapshot(snapshot: MetricSnapshot) {
            gridAdapter.updateSnapshot(snapshot)
        }

        fun setEditMode(enabled: Boolean, config: BridgeConfig?) {
            config ?: return
            gridAdapter.setEditMode(enabled)

            val editScale = 0.72f
            val duration = 350L

            if (enabled) {
                editToolbar.alpha = 0f
                editToolbar.visibility = View.VISIBLE
                editToolbar.animate().alpha(1f).setDuration(duration).start()
                emptyState.visibility = View.GONE
                gaugeHost.visibility = View.VISIBLE
                updateLayoutSelector(config)

                // Zoom out
                gaugeHost.animate()
                    .scaleX(editScale).scaleY(editScale)
                    .setDuration(duration)
                    .setInterpolator(android.view.animation.DecelerateInterpolator())
                    .start()

                // Dark overlay fade in
                editOverlay.visibility = View.VISIBLE
                editOverlay.alpha = 0f
                editOverlay.animate().alpha(1f).setDuration(duration).start()
            } else {
                editToolbar.animate().alpha(0f).setDuration(200L).withEndAction {
                    editToolbar.visibility = View.GONE
                }.start()

                // Zoom back in
                gaugeHost.animate()
                    .scaleX(1f).scaleY(1f)
                    .setDuration(duration)
                    .setInterpolator(android.view.animation.DecelerateInterpolator())
                    .start()

                // Dark overlay fade out
                editOverlay.animate().alpha(0f).setDuration(200L).withEndAction {
                    editOverlay.visibility = View.GONE
                }.start()

                if (config.pages.isNotEmpty()) {
                    val safeIdx = currentPageIndex.coerceIn(0, (config.pages.size - 1).coerceAtLeast(0))
                    if (gaugePager.currentItem != safeIdx) gaugePager.setCurrentItem(safeIdx, false)
                } else {
                    gaugeHost.visibility = View.GONE
                    emptyState.visibility = View.VISIBLE
                }
            }
        }

        private fun updateLayoutSelector(config: BridgeConfig?) {
            val pages = config?.pages ?: return
            if (currentPageIndex in pages.indices) {
                val page = pages[currentPageIndex]
                layoutSelector.text = DashPage.layoutLabel(page.columns, page.rows)
            }
        }

    }

    // ──────── Apps Page ────────

    class AppsPageHolder(view: View) : RecyclerView.ViewHolder(view) {
        private val appsRecycler: RecyclerView = view.findViewById(R.id.appsRecycler)
        private val doneBtn: TextView = view.findViewById(R.id.appsDoneBtn)
        private val title: TextView = view.findViewById(R.id.appsTitle)
        private var isSetup = false
        private var editMode = false
        private lateinit var adapter: AppGridAdapter
        private var onPickCustomIcon: ((String) -> Unit)? = null

        fun setup(onPickCustomIcon: (String) -> Unit) {
            if (isSetup) return
            isSetup = true
            this.onPickCustomIcon = onPickCustomIcon

            val ctx = itemView.context

            adapter = AppGridAdapter(
                onClick = { appInfo ->
                    ctx.packageManager.getLaunchIntentForPackage(appInfo.packageName)?.let {
                        ctx.startActivity(it)
                    }
                },
                onEditClick = { appInfo -> showAppActionDialog(ctx, appInfo) },
                onLongPress = { enterEditMode() }
            )
            loadAndUpdateAdapter(ctx)

            appsRecycler.layoutManager = GridLayoutManager(ctx, 3, GridLayoutManager.HORIZONTAL, false)
            appsRecycler.adapter = adapter

            itemView.setOnLongClickListener {
                if (!editMode) enterEditMode()
                true
            }

            doneBtn.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                exitEditMode()
            }
        }

        fun refreshApps() {
            loadAndUpdateAdapter(itemView.context)
        }

        private fun loadAndUpdateAdapter(ctx: Context) {
            Thread {
                val apps = loadInstalledApps(ctx)
                val hidden = AppPrefs.getHiddenApps(ctx)
                appsRecycler.post {
                    adapter.updateData(apps, hidden)
                }
            }.start()
        }

        private fun enterEditMode() {
            if (editMode) return
            editMode = true
            itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
            adapter.setEditMode(true)
            doneBtn.visibility = View.VISIBLE
            doneBtn.alpha = 0f
            doneBtn.animate().alpha(1f).setDuration(200L).start()
            title.text = "Modifier"
        }

        fun exitEditMode(): Boolean {
            if (!editMode) return false
            editMode = false
            adapter.setEditMode(false)
            doneBtn.animate().alpha(0f).setDuration(150L).withEndAction {
                doneBtn.visibility = View.GONE
            }.start()
            title.text = "Apps"
            return true
        }

        // ── Dialog helpers (settings-chip style) ──

        private fun dialogChipRow(
            ctx: Context, label: String, iconRes: Int,
            textColor: Int = 0xFFF1F5FF.toInt(), accentColor: Int = textColor,
            enabled: Boolean = true
        ): FrameLayout {
            val wrap = FrameLayout(ctx).apply {
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ctx.dp(36)
                ).apply {
                    topMargin = ctx.dp(4)
                    marginStart = ctx.dp(6); marginEnd = ctx.dp(6)
                }
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                alpha = if (enabled) 1f else 0.3f
                isClickable = enabled
                isFocusable = enabled
            }
            val row = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER
                layoutParams = FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
                )
            }
            val iv = ImageView(ctx).apply {
                setImageResource(iconRes)
                val s = ctx.dp(16)
                layoutParams = android.widget.LinearLayout.LayoutParams(s, s).apply { marginEnd = ctx.dp(8) }
                setColorFilter(accentColor)
            }
            val tv = TextView(ctx).apply {
                text = label; textSize = 11f; setTextColor(textColor)
            }
            row.addView(iv); row.addView(tv)
            wrap.addView(row)
            return wrap
        }

        // ── Action Dialog ──

        private fun showAppActionDialog(ctx: Context, app: AppInfo) {
            val isHidden = app.packageName in (AppPrefs.getHiddenApps(ctx))
            val dialog = createWatchDialog(ctx)

            val root = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.VERTICAL
                background = ctx.getDrawable(R.drawable.bg_watch_dialog)
                setPadding(ctx.dp(10), ctx.dp(12), ctx.dp(10), ctx.dp(12))
            }

            // Title
            root.addView(TextView(ctx).apply {
                text = app.label
                textSize = 10f
                gravity = Gravity.CENTER
                setTextColor(0xFF6B7588.toInt())
                setPadding(0, ctx.dp(2), 0, ctx.dp(4))
            })

            // Hide/Show
            val hideChip = dialogChipRow(ctx,
                if (isHidden) "Afficher" else "Cacher",
                if (isHidden) R.drawable.ic_visibility else R.drawable.ic_visibility_off
            )
            hideChip.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss()
                AppPrefs.toggleHiddenApp(ctx, app.packageName)
                loadAndUpdateAdapter(ctx)
            }
            root.addView(hideChip)

            // Rename
            val renameChip = dialogChipRow(ctx, "Renommer", R.drawable.ic_edit)
            renameChip.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss(); showRenameDialog(ctx, app)
            }
            root.addView(renameChip)

            // Change icon
            val iconChip = dialogChipRow(ctx, "Changer l'icône", R.drawable.ic_palette)
            iconChip.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss(); showIconPickerDialog(ctx, app)
            }
            root.addView(iconChip)

            // Uninstall
            val uninstallChip = dialogChipRow(ctx, "Désinstaller", R.drawable.ic_trash,
                textColor = 0xFFFF6B6B.toInt(), accentColor = 0xFFFF6B6B.toInt(),
                enabled = !app.isSystem)
            uninstallChip.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss()
                val intent = Intent(Intent.ACTION_DELETE).apply {
                    data = Uri.fromParts("package", app.packageName, null)
                }
                ctx.startActivity(intent)
            }
            root.addView(uninstallChip)

            dialog.setContentView(root)
            configureDialogWindow(dialog, ctx)
            dialog.show()
        }

        // ── Rename Dialog ──

        private fun showRenameDialog(ctx: Context, app: AppInfo) {
            val dialog = createWatchDialog(ctx)

            val root = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.VERTICAL
                background = ctx.getDrawable(R.drawable.bg_watch_dialog)
                setPadding(ctx.dp(14), ctx.dp(12), ctx.dp(14), ctx.dp(12))
            }

            root.addView(TextView(ctx).apply {
                text = "Renommer"
                textSize = 10f
                gravity = Gravity.CENTER
                setTextColor(0xFF6B7588.toInt())
                setPadding(0, ctx.dp(2), 0, ctx.dp(8))
            })

            // Input wrapped in a chip
            val inputChip = FrameLayout(ctx).apply {
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
                )
            }
            val input = EditText(ctx).apply {
                setText(app.label)
                setTextColor(0xFFF1F5FF.toInt())
                setHintTextColor(0xFF6B7588.toInt())
                setSelection(text.length)
                textSize = 12f
                isSingleLine = true
                background = null
                val p = ctx.dp(12)
                setPadding(p, ctx.dp(9), p, ctx.dp(9))
            }
            inputChip.addView(input)
            root.addView(inputChip)

            // Buttons
            val btnRow = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply { topMargin = ctx.dp(6) }
            }

            val resetBtn = TextView(ctx).apply {
                text = "Réinit."
                textSize = 10f
                setTextColor(0xFFFF6B6B.toInt())
                gravity = Gravity.CENTER
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                setPadding(ctx.dp(10), ctx.dp(8), ctx.dp(10), ctx.dp(8))
                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    dialog.dismiss()
                    AppPrefs.setCustomLabel(ctx, app.packageName, null)
                    loadAndUpdateAdapter(ctx)
                }
            }
            btnRow.addView(resetBtn, android.widget.LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
            ).apply { marginEnd = ctx.dp(4) })

            val cancelBtn = TextView(ctx).apply {
                text = "Annuler"
                textSize = 10f
                setTextColor(0xFF6B7588.toInt())
                gravity = Gravity.CENTER
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                setPadding(ctx.dp(10), ctx.dp(8), ctx.dp(10), ctx.dp(8))
                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    dialog.dismiss()
                }
            }
            btnRow.addView(cancelBtn, android.widget.LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
            ).apply { marginEnd = ctx.dp(4) })

            val okBtn = TextView(ctx).apply {
                text = "OK"
                textSize = 10f
                setTextColor(0xFF49D6FF.toInt())
                gravity = Gravity.CENTER
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                setPadding(ctx.dp(10), ctx.dp(8), ctx.dp(10), ctx.dp(8))
                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    val newName = input.text.toString().trim()
                    if (newName.isNotBlank()) {
                        AppPrefs.setCustomLabel(ctx, app.packageName, newName)
                        loadAndUpdateAdapter(ctx)
                    }
                    dialog.dismiss()
                }
            }
            btnRow.addView(okBtn, android.widget.LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
            ))

            root.addView(btnRow)

            dialog.setContentView(root)
            configureDialogWindow(dialog, ctx)
            dialog.window?.setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
            dialog.show()
        }

        // ── Icon Picker Dialog ──

        private fun showIconPickerDialog(ctx: Context, app: AppInfo) {
            val dialog = createWatchDialog(ctx)

            val root = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.VERTICAL
                background = ctx.getDrawable(R.drawable.bg_watch_dialog)
                setPadding(ctx.dp(10), ctx.dp(12), ctx.dp(10), ctx.dp(12))
            }

            root.addView(TextView(ctx).apply {
                text = "Changer l'icône"
                textSize = 10f
                gravity = Gravity.CENTER
                setTextColor(0xFF6B7588.toInt())
                setPadding(0, ctx.dp(2), 0, ctx.dp(4))
            })

            // Custom image
            val imageChip = dialogChipRow(ctx, "Image personnalisée", R.drawable.ic_image)
            imageChip.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss(); onPickCustomIcon?.invoke(app.packageName)
            }
            root.addView(imageChip)

            // Reset
            if (AppPrefs.getCustomIcon(ctx, app.packageName) != null) {
                val resetChip = dialogChipRow(ctx, "Réinitialiser", R.drawable.ic_refresh,
                    textColor = 0xFFFF6B6B.toInt(), accentColor = 0xFFFF6B6B.toInt())
                resetChip.setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    dialog.dismiss()
                    AppPrefs.setCustomIcon(ctx, app.packageName, null)
                    loadAndUpdateAdapter(ctx)
                }
                root.addView(resetChip)
            }

            // Loader for icon packs
            val packsLoader = android.widget.ProgressBar(ctx).apply {
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply {
                    gravity = Gravity.CENTER_HORIZONTAL
                    topMargin = ctx.dp(12)
                    bottomMargin = ctx.dp(8)
                }
                isIndeterminate = true
            }
            root.addView(packsLoader)

            dialog.setContentView(root)
            configureDialogWindow(dialog, ctx)
            dialog.show()

            // Load icon packs in background
            Thread {
                val iconPacks = IconPackHelper.findInstalledPacks(ctx)
                root.post {
                    packsLoader.visibility = View.GONE
                    for (pack in iconPacks) {
                        val packChip = dialogChipRow(ctx, pack.label, R.drawable.ic_apps)
                        packChip.setOnClickListener {
                            it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                            dialog.dismiss(); showIconPackGrid(ctx, app, pack)
                        }
                        root.addView(packChip)
                    }
                    if (iconPacks.isEmpty()) {
                        root.addView(TextView(ctx).apply {
                            text = "Aucun pack d'icônes installé"
                            textSize = 9f
                            gravity = Gravity.CENTER
                            setTextColor(0xFF6B7588.toInt())
                            setPadding(0, ctx.dp(8), 0, ctx.dp(4))
                        })
                    }
                }
            }.start()
        }

        private fun showIconPackGrid(ctx: Context, app: AppInfo, pack: IconPackHelper.IconPackInfo) {
            val res = try {
                ctx.packageManager.getResourcesForApplication(pack.packageName)
            } catch (_: Exception) { return }

            val dialog = Dialog(ctx, android.R.style.Theme_DeviceDefault_NoActionBar_Fullscreen)

            // All entries will be loaded async
            var allEntries = emptyList<IconPackHelper.IconEntry>()
            var filteredEntries = emptyList<IconPackHelper.IconEntry>()

            val root = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.VERTICAL
                setBackgroundColor(0xFF000000.toInt())
            }

            // ── Top bar: title + close ──
            val topBar = FrameLayout(ctx).apply {
                val lp = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
                )
                lp.topMargin = ctx.dp(6)
                layoutParams = lp
            }
            val titleView = TextView(ctx).apply {
                text = pack.label
                textSize = 10f
                setTextColor(0xFF6B7588.toInt())
                layoutParams = FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply { gravity = Gravity.CENTER_HORIZONTAL }
            }
            topBar.addView(titleView)
            val closeBtn = ImageView(ctx).apply {
                setImageResource(R.drawable.ic_close)
                val s = ctx.dp(28)
                layoutParams = FrameLayout.LayoutParams(s, s).apply {
                    gravity = Gravity.END or Gravity.CENTER_VERTICAL
                    marginEnd = ctx.dp(8)
                }
                setBackgroundResource(R.drawable.bg_nav_button)
                val p = ctx.dp(5)
                setPadding(p, p, p, p)
                setOnClickListener { dialog.dismiss() }
            }
            topBar.addView(closeBtn)
            root.addView(topBar)

            // ── Suggested icon row (placeholder, filled async) ──
            val suggestRow = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER_VERTICAL
                val lp = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
                )
                lp.topMargin = ctx.dp(4)
                lp.bottomMargin = ctx.dp(2)
                lp.marginStart = ctx.dp(12)
                lp.marginEnd = ctx.dp(12)
                layoutParams = lp
                setBackgroundColor(0xFF1A1A2E.toInt())
                val padV = ctx.dp(6)
                val padH = ctx.dp(10)
                setPadding(padH, padV, padH, padV)
                visibility = View.GONE
            }
            val suggestLabel = TextView(ctx).apply {
                text = "Suggéré"
                textSize = 9f
                setTextColor(0xFF6B7588.toInt())
            }
            suggestRow.addView(suggestLabel, android.widget.LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
            ))
            val suggestIv = ImageView(ctx).apply {
                val s = ctx.dp(42)
                layoutParams = android.widget.LinearLayout.LayoutParams(s, s)
                scaleType = ImageView.ScaleType.FIT_CENTER
                val p = ctx.dp(4)
                setPadding(p, p, p, p)
            }
            suggestRow.addView(suggestIv)
            root.addView(suggestRow)

            // ── Search bar ──
            val searchInput = EditText(ctx).apply {
                hint = "Rechercher…"
                setHintTextColor(0xFF555555.toInt())
                setTextColor(0xFFFFFFFF.toInt())
                textSize = 12f
                isSingleLine = true
                setBackgroundColor(0xFF1A1A2E.toInt())
                val padH = ctx.dp(12)
                val padV = ctx.dp(6)
                setPadding(padH, padV, padH, padV)
                val lp = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
                )
                lp.topMargin = ctx.dp(4)
                lp.marginStart = ctx.dp(12)
                lp.marginEnd = ctx.dp(12)
                layoutParams = lp
            }
            root.addView(searchInput)

            // ── Grid recycler ──
            val recycler = RecyclerView(ctx).apply {
                layoutManager = GridLayoutManager(ctx, 5)
                clipToPadding = false
                setPadding(ctx.dp(4), ctx.dp(6), ctx.dp(4), ctx.dp(8))
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f
                )
                setItemViewCacheSize(5)
                recycledViewPool.setMaxRecycledViews(0, 10)
                setHasFixedSize(true)
            }

            val PAGE_SIZE = 15
            var displayedCount = PAGE_SIZE.coerceAtMost(filteredEntries.size)
            var isLoadingMore = false

            val gridAdapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
                inner class VH(val iv: ImageView) : RecyclerView.ViewHolder(iv)

                override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
                    val size = ctx.dp(76)
                    val iv = ImageView(ctx).apply {
                        layoutParams = ViewGroup.LayoutParams(size, size)
                        scaleType = ImageView.ScaleType.FIT_CENTER
                        val p = ctx.dp(10)
                        setPadding(p, p, p, p)
                    }
                    return VH(iv)
                }

                override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
                    val entry = filteredEntries[position]
                    val vh = holder as VH
                    // Recycle previous drawable to free memory
                    vh.iv.setImageDrawable(null)
                    try {
                        @Suppress("DEPRECATION")
                        vh.iv.setImageDrawable(res.getDrawable(entry.drawableId, null))
                    } catch (_: Exception) {
                        vh.iv.setImageDrawable(null)
                    }
                    vh.iv.setOnClickListener {
                        it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                        AppPrefs.setCustomIcon(ctx, app.packageName, "iconpack:${pack.packageName}:${entry.name}")
                        loadAndUpdateAdapter(ctx)
                        dialog.dismiss()
                    }
                }

                override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
                    (holder as? VH)?.iv?.setImageDrawable(null)
                }

                override fun getItemCount() = displayedCount
            }

            recycler.adapter = gridAdapter

            // Infinite scroll: load 15 more when near bottom
            recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
                override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
                    if (isLoadingMore || displayedCount >= filteredEntries.size) return
                    val lm = rv.layoutManager as GridLayoutManager
                    val lastVisible = lm.findLastVisibleItemPosition()
                    if (lastVisible >= displayedCount - 5) {
                        isLoadingMore = true
                        val oldCount = displayedCount
                        displayedCount = (displayedCount + PAGE_SIZE).coerceAtMost(filteredEntries.size)
                        gridAdapter.notifyItemRangeInserted(oldCount, displayedCount - oldCount)
                        isLoadingMore = false
                    }
                }
            })

            searchInput.addTextChangedListener(object : android.text.TextWatcher {
                override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
                override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
                override fun afterTextChanged(s: android.text.Editable?) {
                    val query = s?.toString()?.trim()?.lowercase() ?: ""
                    filteredEntries = if (query.isEmpty()) {
                        allEntries
                    } else {
                        allEntries.filter { it.name.lowercase().contains(query) }
                    }
                    displayedCount = PAGE_SIZE.coerceAtMost(filteredEntries.size)
                    gridAdapter.notifyDataSetChanged()
                }
            })

            // Loader centered over the grid
            val loader = android.widget.ProgressBar(ctx).apply {
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply {
                    gravity = Gravity.CENTER_HORIZONTAL
                    topMargin = ctx.dp(40)
                }
                isIndeterminate = true
            }
            root.addView(loader)
            root.addView(recycler)

            // Hide grid + search until loaded
            recycler.visibility = View.GONE
            searchInput.visibility = View.GONE

            dialog.setContentView(root)
            dialog.show()

            // Load icon entries on background thread, then update UI
            Thread {
                val loaded = IconPackHelper.loadIcons(ctx, pack.packageName)
                val suggestedName = IconPackHelper.findSuggestedDrawable(ctx, pack.packageName, app.packageName)

                recycler.post {
                    allEntries = loaded
                    filteredEntries = loaded
                    displayedCount = PAGE_SIZE.coerceAtMost(filteredEntries.size)
                    gridAdapter.notifyDataSetChanged()

                    // Hide loader, show content
                    loader.visibility = View.GONE
                    recycler.visibility = View.VISIBLE
                    searchInput.visibility = View.VISIBLE

                    // Show suggested icon if found
                    if (suggestedName != null) {
                        val entry = loaded.firstOrNull { it.name == suggestedName }
                        if (entry != null) {
                            try {
                                @Suppress("DEPRECATION")
                                suggestIv.setImageDrawable(res.getDrawable(entry.drawableId, null))
                            } catch (_: Exception) {}
                            suggestIv.setOnClickListener {
                                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                                AppPrefs.setCustomIcon(ctx, app.packageName, "iconpack:${pack.packageName}:${entry.name}")
                                loadAndUpdateAdapter(ctx)
                                dialog.dismiss()
                            }
                            suggestRow.visibility = View.VISIBLE
                        }
                    }
                }
            }.start()
        }

        // ── Data ──

        private fun loadInstalledApps(ctx: Context): List<AppInfo> {
            val mainIntent = Intent(Intent.ACTION_MAIN, null).apply {
                addCategory(Intent.CATEGORY_LAUNCHER)
            }
            val resolveInfos: List<ResolveInfo> = if (Build.VERSION.SDK_INT >= 33) {
                ctx.packageManager.queryIntentActivities(mainIntent, PackageManager.ResolveInfoFlags.of(0))
            } else {
                @Suppress("DEPRECATION")
                ctx.packageManager.queryIntentActivities(mainIntent, 0)
            }
            return resolveInfos
                .filter { it.activityInfo.packageName != ctx.packageName }
                .map { ri ->
                    val pkg = ri.activityInfo.packageName
                    val customLabel = AppPrefs.getCustomLabel(ctx, pkg)
                    val customIconRef = AppPrefs.getCustomIcon(ctx, pkg)
                    val icon = if (customIconRef != null) {
                        resolveCustomIcon(ctx, customIconRef) ?: ri.loadIcon(ctx.packageManager)
                    } else {
                        ri.loadIcon(ctx.packageManager)
                    }
                    val appFlags = try {
                        if (Build.VERSION.SDK_INT >= 33) {
                            ctx.packageManager.getApplicationInfo(pkg, PackageManager.ApplicationInfoFlags.of(0)).flags
                        } else {
                            @Suppress("DEPRECATION")
                            ctx.packageManager.getApplicationInfo(pkg, 0).flags
                        }
                    } catch (_: Exception) { 0 }
                    val isSystem = (appFlags and android.content.pm.ApplicationInfo.FLAG_SYSTEM) != 0
                    AppInfo(
                        label = customLabel ?: ri.loadLabel(ctx.packageManager).toString(),
                        packageName = pkg,
                        icon = icon,
                        isSystem = isSystem
                    )
                }
                .sortedBy { it.label.lowercase() }
        }

        private fun resolveCustomIcon(ctx: Context, iconRef: String): Drawable? {
            return when {
                iconRef.startsWith("file:") -> {
                    Drawable.createFromPath(iconRef.removePrefix("file:"))
                }
                iconRef.startsWith("iconpack:") -> {
                    val parts = iconRef.removePrefix("iconpack:").split(":", limit = 2)
                    if (parts.size == 2) IconPackHelper.loadDrawable(ctx, parts[0], parts[1]) else null
                }
                else -> null
            }
        }

        data class AppInfo(val label: String, val packageName: String, val icon: Drawable, val isSystem: Boolean = false)

        class AppGridAdapter(
            private val onClick: (AppInfo) -> Unit,
            private val onEditClick: (AppInfo) -> Unit,
            private val onLongPress: () -> Unit
        ) : RecyclerView.Adapter<AppGridAdapter.VH>() {

            private var editMode = false
            private var allApps: List<AppInfo> = emptyList()
            private var hiddenApps: Set<String> = emptySet()
            private var displayedApps: List<AppInfo> = emptyList()

            fun updateData(apps: List<AppInfo>, hidden: Set<String>) {
                allApps = apps
                hiddenApps = hidden
                displayedApps = computeDisplayed()
                notifyDataSetChanged()
            }

            private fun computeDisplayed(): List<AppInfo> =
                if (editMode) allApps else allApps.filter { it.packageName !in hiddenApps }

            fun setEditMode(enabled: Boolean) {
                editMode = enabled
                displayedApps = computeDisplayed()
                notifyDataSetChanged()
            }

            class VH(view: View) : RecyclerView.ViewHolder(view) {
                val icon: ImageView = view.findViewById(R.id.appIcon)
                val name: TextView = view.findViewById(R.id.appName)
                val editOverlay: View = view.findViewById(R.id.editOverlay)
                val editBadge: ImageView = view.findViewById(R.id.editBadge)
            }

            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = VH(
                LayoutInflater.from(parent.context).inflate(R.layout.item_app_entry, parent, false)
            )

            override fun onBindViewHolder(holder: VH, position: Int) {
                val app = displayedApps[position]
                holder.icon.setImageDrawable(app.icon)
                holder.name.text = app.label
                val isHidden = app.packageName in hiddenApps

                if (editMode) {
                    holder.itemView.alpha = if (isHidden) 0.3f else 1f
                    holder.editOverlay.visibility = View.VISIBLE
                    holder.editBadge.visibility = View.VISIBLE
                    holder.editBadge.setColorFilter(0xFFFFFFFF.toInt())
                    startWobble(holder.itemView)
                    holder.itemView.setOnClickListener {
                        it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                        onEditClick(app)
                    }
                    holder.itemView.setOnLongClickListener(null)
                    holder.itemView.isLongClickable = false
                } else {
                    holder.itemView.alpha = 1f
                    holder.editOverlay.visibility = View.GONE
                    holder.editBadge.visibility = View.GONE
                    stopWobble(holder.itemView)
                    holder.itemView.setOnClickListener {
                        it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                        onClick(app)
                    }
                    holder.itemView.setOnLongClickListener {
                        it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
                        onLongPress()
                        true
                    }
                }
            }

            override fun onViewRecycled(holder: VH) {
                stopWobble(holder.itemView)
                holder.editOverlay.visibility = View.GONE
                holder.editBadge.visibility = View.GONE
            }

            override fun getItemCount() = displayedApps.size

            private fun startWobble(view: View) {
                if (view.tag is ObjectAnimator) return
                val anim = ObjectAnimator.ofFloat(view, View.ROTATION, -1.5f, 1.5f).apply {
                    duration = 100 + (Math.random() * 60).toLong()
                    repeatMode = ValueAnimator.REVERSE
                    repeatCount = ValueAnimator.INFINITE
                    interpolator = LinearInterpolator()
                }
                view.tag = anim
                anim.start()
            }

            private fun stopWobble(view: View) {
                (view.tag as? ObjectAnimator)?.cancel()
                view.rotation = 0f
                view.tag = null
            }
        }
    }

    // ──────── Settings Page ────────

    class SettingsPageHolder(view: View) : RecyclerView.ViewHolder(view) {
        private val setupButton: TextView = view.findViewById(R.id.sSetupButton)
        private val screenChargeSwitch: com.google.android.material.switchmaterial.SwitchMaterial =
            view.findViewById(R.id.sScreenChargeControl)
        private val wallpaperButton: TextView = view.findViewById(R.id.sWallpaperButton)
        private val dpiMinus: ImageView = view.findViewById(R.id.sDpiMinus)
        private val dpiPlus: ImageView = view.findViewById(R.id.sDpiPlus)
        private val dpiValue: TextView = view.findViewById(R.id.sDpiValue)

        private var isSetup = false
        private lateinit var onSettingChanged: () -> Unit

        fun setup(
            config: BridgeConfig?, onSettingChanged: () -> Unit,
            onWallpaperPick: () -> Unit, onWallpaperColor: (String) -> Unit, onWallpaperReset: () -> Unit
        ) {
            if (isSetup) return
            isSetup = true
            this.onSettingChanged = onSettingChanged
            val cfg = config ?: return
            val ctx = itemView.context

            setupButton.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                ctx.startActivity(Intent(ctx, SetupActivity::class.java))
            }

            wallpaperButton.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                showWallpaperDialog(ctx, onWallpaperPick, onWallpaperColor, onWallpaperReset)
            }

            screenChargeSwitch.setOnCheckedChangeListener(null)
            screenChargeSwitch.isChecked = AppPrefs.getScreenChargeControl(ctx)
            screenChargeSwitch.setOnCheckedChangeListener { _, on ->
                if (on) {
                    // Check if device admin is enabled, prompt if not
                    val dpm = ctx.getSystemService(Activity.DEVICE_POLICY_SERVICE)
                        as DevicePolicyManager
                    val admin = ComponentName(ctx, ScreenAdminReceiver::class.java)
                    if (!dpm.isAdminActive(admin)) {
                        val intent = Intent(
                            DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN
                        ).apply {
                            putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin)
                            putExtra(
                                DevicePolicyManager.EXTRA_ADD_EXPLANATION,
                                "Permet d'eteindre l'ecran quand le chargeur est debranche"
                            )
                        }
                        ctx.startActivity(intent)
                        // Don't save yet — user might cancel. Revert switch.
                        screenChargeSwitch.isChecked = false
                        return@setOnCheckedChangeListener
                    }
                }
                AppPrefs.setScreenChargeControl(ctx, on)
            }

            updateDpiDisplay(ctx)
            dpiMinus.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                adjustDpi(ctx, -20)
            }
            dpiPlus.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                adjustDpi(ctx, 20)
            }
            dpiValue.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                showDpiInputDialog(ctx)
            }
        }

        fun updateConfig(config: BridgeConfig) {
            val ctx = itemView.context
            screenChargeSwitch.setOnCheckedChangeListener(null)
            screenChargeSwitch.isChecked = AppPrefs.getScreenChargeControl(ctx)
            screenChargeSwitch.setOnCheckedChangeListener { _, on ->
                AppPrefs.setScreenChargeControl(ctx, on)
            }
        }

        private fun adjustDpi(ctx: Context, delta: Int) {
            val current = AppPrefs.getCustomDpi(ctx)
            val systemDpi = ctx.resources.configuration.densityDpi
            val base = if (current > 0) current else systemDpi
            val newDpi = (base + delta).coerceIn(120, 640)
            AppPrefs.setCustomDpi(ctx, newDpi)
            updateDpiDisplay(ctx)
            (ctx as? Activity)?.recreate()
        }

        private fun updateDpiDisplay(ctx: Context) {
            val dpi = AppPrefs.getCustomDpi(ctx)
            dpiValue.text = "$dpi"
        }

        private fun showDpiInputDialog(ctx: Context) {
            val dialog = createWatchDialog(ctx)

            val root = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.VERTICAL
                background = ctx.getDrawable(R.drawable.bg_watch_dialog)
                setPadding(ctx.dp(14), ctx.dp(12), ctx.dp(14), ctx.dp(12))
            }

            root.addView(TextView(ctx).apply {
                text = "Densité (DPI)"
                textSize = 10f
                gravity = Gravity.CENTER
                setTextColor(0xFF6B7588.toInt())
                setPadding(0, ctx.dp(2), 0, ctx.dp(8))
            })

            val inputChip = FrameLayout(ctx).apply {
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
                )
            }
            val input = EditText(ctx).apply {
                inputType = android.text.InputType.TYPE_CLASS_NUMBER
                setText(AppPrefs.getCustomDpi(ctx).toString())
                setTextColor(0xFFF1F5FF.toInt())
                setHintTextColor(0xFF6B7588.toInt())
                hint = "100–800"
                setSelection(text.length)
                textSize = 12f
                isSingleLine = true
                background = null
                val p = ctx.dp(12)
                setPadding(p, ctx.dp(9), p, ctx.dp(9))
                setSelectAllOnFocus(true)
            }
            inputChip.addView(input)
            root.addView(inputChip)

            val btnRow = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply { topMargin = ctx.dp(6) }
            }

            val cancelBtn = TextView(ctx).apply {
                text = "Annuler"
                textSize = 10f
                setTextColor(0xFF6B7588.toInt())
                gravity = Gravity.CENTER
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                setPadding(ctx.dp(10), ctx.dp(8), ctx.dp(10), ctx.dp(8))
                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    dialog.dismiss()
                }
            }
            btnRow.addView(cancelBtn, android.widget.LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
            ).apply { marginEnd = ctx.dp(4) })

            val okBtn = TextView(ctx).apply {
                text = "OK"
                textSize = 10f
                setTextColor(0xFF49D6FF.toInt())
                gravity = Gravity.CENTER
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
                setPadding(ctx.dp(10), ctx.dp(8), ctx.dp(10), ctx.dp(8))
                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    val value = input.text.toString().toIntOrNull()
                    if (value != null && value in 100..800) {
                        AppPrefs.setCustomDpi(ctx, value)
                        updateDpiDisplay(ctx)
                        dialog.dismiss()
                        (ctx as? Activity)?.recreate()
                    }
                }
            }
            btnRow.addView(okBtn, android.widget.LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
            ))

            root.addView(btnRow)

            dialog.setContentView(root)
            configureDialogWindow(dialog, ctx)
            dialog.window?.setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
            dialog.show()
        }

        private fun showWallpaperDialog(
            ctx: Context,
            onWallpaperPick: () -> Unit,
            onWallpaperColor: (String) -> Unit,
            onWallpaperReset: () -> Unit
        ) {
            val dialog = Dialog(ctx).apply {
                requestWindowFeature(android.view.Window.FEATURE_NO_TITLE)
                setCancelable(true)
                setCanceledOnTouchOutside(true)
            }

            val root = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.VERTICAL
                background = ctx.getDrawable(R.drawable.bg_watch_dialog)
                setPadding(ctx.dp(10), ctx.dp(12), ctx.dp(10), ctx.dp(12))
            }

            root.addView(TextView(ctx).apply {
                text = "Fond d'écran"
                textSize = 10f
                gravity = Gravity.CENTER
                setTextColor(0xFF6B7588.toInt())
                setPadding(0, ctx.dp(2), 0, ctx.dp(4))
            })

            // Image picker
            val imageChip = FrameLayout(ctx).apply {
                layoutParams = android.widget.LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ctx.dp(36)
                ).apply { topMargin = ctx.dp(4); marginStart = ctx.dp(6); marginEnd = ctx.dp(6) }
                background = ctx.getDrawable(R.drawable.bg_settings_chip)
            }
            val imageRow = android.widget.LinearLayout(ctx).apply {
                orientation = android.widget.LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER
                layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
            }
            imageRow.addView(ImageView(ctx).apply {
                setImageResource(R.drawable.ic_image)
                val s = ctx.dp(16)
                layoutParams = android.widget.LinearLayout.LayoutParams(s, s).apply { marginEnd = ctx.dp(8) }
                setColorFilter(0xFFF1F5FF.toInt())
            })
            imageRow.addView(TextView(ctx).apply { text = "Image"; textSize = 11f; setTextColor(0xFFF1F5FF.toInt()) })
            imageChip.addView(imageRow)
            imageChip.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss(); onWallpaperPick()
            }
            root.addView(imageChip)

            // Color presets
            val colors = listOf(
                "Noir" to "#FF000000",
                "Gris foncé" to "#FF1A1A2E",
                "Bleu nuit" to "#FF0D1B2A",
                "Vert foncé" to "#FF0A1F0A",
                "Rouge foncé" to "#FF1A0A0A"
            )
            for ((label, hex) in colors) {
                val chip = FrameLayout(ctx).apply {
                    layoutParams = android.widget.LinearLayout.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT, ctx.dp(36)
                    ).apply { topMargin = ctx.dp(4); marginStart = ctx.dp(6); marginEnd = ctx.dp(6) }
                    background = ctx.getDrawable(R.drawable.bg_settings_chip)
                }
                val colorRow = android.widget.LinearLayout(ctx).apply {
                    orientation = android.widget.LinearLayout.HORIZONTAL
                    gravity = Gravity.CENTER
                    layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
                }
                val swatch = View(ctx).apply {
                    val s = ctx.dp(14)
                    layoutParams = android.widget.LinearLayout.LayoutParams(s, s).apply { marginEnd = ctx.dp(8) }
                    setBackgroundColor(android.graphics.Color.parseColor(hex))
                }
                colorRow.addView(swatch)
                colorRow.addView(TextView(ctx).apply { text = label; textSize = 11f; setTextColor(0xFFF1F5FF.toInt()) })
                chip.addView(colorRow)
                chip.setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    dialog.dismiss(); onWallpaperColor(hex)
                }
                root.addView(chip)
            }

            // Reset
            if (AppPrefs.getWallpaper(ctx) != null) {
                val resetChip = FrameLayout(ctx).apply {
                    layoutParams = android.widget.LinearLayout.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT, ctx.dp(36)
                    ).apply { topMargin = ctx.dp(4); marginStart = ctx.dp(6); marginEnd = ctx.dp(6) }
                    background = ctx.getDrawable(R.drawable.bg_settings_chip)
                }
                val resetRow = android.widget.LinearLayout(ctx).apply {
                    orientation = android.widget.LinearLayout.HORIZONTAL
                    gravity = Gravity.CENTER
                    layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
                }
                resetRow.addView(ImageView(ctx).apply {
                    setImageResource(R.drawable.ic_refresh)
                    val s = ctx.dp(16)
                    layoutParams = android.widget.LinearLayout.LayoutParams(s, s).apply { marginEnd = ctx.dp(8) }
                    setColorFilter(0xFFFF6B6B.toInt())
                })
                resetRow.addView(TextView(ctx).apply { text = "Réinitialiser"; textSize = 11f; setTextColor(0xFFFF6B6B.toInt()) })
                resetChip.addView(resetRow)
                resetChip.setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    dialog.dismiss(); onWallpaperReset()
                }
                root.addView(resetChip)
            }

            val scrollWrapper = android.widget.ScrollView(ctx).apply {
                isVerticalScrollBarEnabled = false
                addView(root)
            }
            dialog.setContentView(scrollWrapper)
            dialog.window?.apply {
                setBackgroundDrawableResource(android.R.color.transparent)
                setLayout(ctx.dp(260), ViewGroup.LayoutParams.WRAP_CONTENT)
                setDimAmount(0.6f)
            }
            dialog.show()
        }
    }
}
