package com.erwan.revwatch

import android.app.Activity
import android.app.Dialog
import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
import android.util.Log
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import com.erwan.revwatch.model.DataBinding
import com.erwan.revwatch.model.GaugeLimits
import com.erwan.revwatch.model.GaugeRange
import com.erwan.revwatch.model.UnitOption
import com.erwan.revwatch.model.VehicleProfile
import com.erwan.revwatch.model.WidgetType
import com.erwan.revwatch.prefs.AppPrefs
import com.erwan.revwatch.model.face.FaceParser
import com.erwan.revwatch.model.face.FaceStore
import com.erwan.revwatch.ui.GaugeView
import com.erwan.revwatch.util.dp

class GaugeConfigActivity : BaseActivity() {

    private var step = 0
    private val totalSteps = 4

    private var widgetIndex = 0
    private var unitIndex = 0
    private var hasPickedType = false
    private var hasPickedFace = false
    private var vehicleProfile: VehicleProfile? = null

    // Range in RAW sensor units
    private var rangeMin = 0.0
    private var rangeMax = 100.0
    private var warnLow: Double? = null
    private var dangerLow: Double? = null
    private var warnHigh: Double? = null
    private var dangerHigh: Double? = null

    private var selectedFaceId: String? = null
    private var selectedCategory: FaceStore.FaceCategory = FaceStore.FaceCategory.ALL

    private var pageIndex = -1
    private var cellIndex = -1

    // Extra data bindings for multi-value gauges
    private val extraBindings = mutableListOf<DataBinding>()

    // Views
    private lateinit var dotViews: List<View>
    private lateinit var stepViews: List<View>
    private lateinit var stepStyle: LinearLayout
    private lateinit var previews: List<GaugeView>
    private lateinit var typeName: TextView
    private lateinit var unitSelector: TextView
    private lateinit var backButton: ImageView
    private lateinit var nextButton: ImageView
    private lateinit var facePickerScroll: ScrollView
    private lateinit var facePickerList: LinearLayout
    private lateinit var typeList: LinearLayout
    private val typeViewTags = mutableListOf<Pair<View, Int>>()

    private val qrScanLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
        val text = result.data?.getStringExtra(QrScanActivity.EXTRA_QR_TEXT)
        if (!text.isNullOrBlank()) {
            importFaceFromText(text)
        }
    }

    private val faceFilePicker = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
        if (uri == null) return@registerForActivityResult
        try {
            val text = contentResolver.openInputStream(uri)?.bufferedReader()?.readText()
            if (text.isNullOrBlank()) {
                Toast.makeText(this, "Fichier vide", Toast.LENGTH_SHORT).show()
                return@registerForActivityResult
            }
            importFaceFromText(text)
        } catch (e: Exception) {
            Log.e("FaceImport", "File read failed", e)
            Toast.makeText(this, "Erreur de lecture: ${e.message}", Toast.LENGTH_SHORT).show()
        }
    }

    // Step 3 fields
    private lateinit var rangeMinInput: EditText
    private lateinit var rangeMaxInput: EditText
    private lateinit var limitWarnLowInput: EditText
    private lateinit var limitDangerLowInput: EditText
    private lateinit var limitWarnHighInput: EditText
    private lateinit var limitDangerHighInput: EditText
    private lateinit var limitWarnLowToggle: ImageView
    private lateinit var limitDangerLowToggle: ImageView
    private lateinit var limitWarnHighToggle: ImageView
    private lateinit var limitDangerHighToggle: ImageView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_gauge_config)
        enableImmersiveMode()

        // Bind views
        dotViews = listOf(
            findViewById(R.id.dot0), findViewById(R.id.dot1),
            findViewById(R.id.dot2), findViewById(R.id.dot3)
        )
        stepStyle = findViewById(R.id.stepStyle)
        stepViews = listOf<View>(
            findViewById(R.id.stepType), stepStyle,
            findViewById(R.id.stepUnit), findViewById(R.id.stepRange)
        )
        previews = listOf(
            findViewById(R.id.previewStep1),
            findViewById(R.id.previewStep2)
        )
        typeList = findViewById(R.id.typeList)
        typeName = TextView(this) // dummy, kept for updatePreview compat
        unitSelector = findViewById(R.id.unitSelector)
        backButton = findViewById(R.id.backButton)
        nextButton = findViewById(R.id.nextButton)

        facePickerScroll = findViewById(R.id.facePickerScroll)
        facePickerList = findViewById(R.id.facePickerList)

        rangeMinInput = findViewById(R.id.rangeMinInput)
        rangeMaxInput = findViewById(R.id.rangeMaxInput)
        limitWarnLowInput = findViewById(R.id.limitWarnLowInput)
        limitDangerLowInput = findViewById(R.id.limitDangerLowInput)
        limitWarnHighInput = findViewById(R.id.limitWarnHighInput)
        limitDangerHighInput = findViewById(R.id.limitDangerHighInput)
        limitWarnLowToggle = findViewById(R.id.limitWarnLowToggle)
        limitDangerLowToggle = findViewById(R.id.limitDangerLowToggle)
        limitWarnHighToggle = findViewById(R.id.limitWarnHighToggle)
        limitDangerHighToggle = findViewById(R.id.limitDangerHighToggle)

        // Load vehicle profile for vehicle-specific defaults
        vehicleProfile = try { AppPrefs.load(this).vehicleProfile } catch (_: Exception) { null }

        // Read intent extras
        pageIndex = intent.getIntExtra(EXTRA_PAGE_INDEX, -1)
        cellIndex = intent.getIntExtra(EXTRA_CELL_INDEX, -1)

        val initialWidgetKey = intent.getStringExtra(EXTRA_WIDGET_KEY)
        val initialUnitKey = intent.getStringExtra(EXTRA_UNIT_KEY)
        val initialRange = GaugeRange.deserialize(intent.getStringExtra(EXTRA_RANGE))
        selectedFaceId = intent.getStringExtra(EXTRA_FACE_ID)

        // Load existing extra bindings
        val bindingsStr = intent.getStringExtra(EXTRA_BINDINGS)
        if (!bindingsStr.isNullOrBlank()) {
            extraBindings.clear()
            extraBindings.addAll(bindingsStr.split(";").mapNotNull { DataBinding.deserialize(it) })
        }

        if (initialWidgetKey != null) {
            widgetIndex = WidgetType.entries.indexOfFirst { it.key == initialWidgetKey }.coerceAtLeast(0)
            hasPickedType = true
            hasPickedFace = selectedFaceId != null
        }
        if (initialUnitKey != null) {
            val w = WidgetType.entries[widgetIndex]
            unitIndex = UnitOption.optionsFor(w).indexOfFirst { it.key == initialUnitKey }.coerceAtLeast(0)
        }

        // Init range from intent or defaults (vehicle-specific if available)
        val range = initialRange ?: GaugeRange.defaultFor(WidgetType.entries[widgetIndex], vehicleProfile)
        rangeMin = range.minValue
        rangeMax = range.maxValue
        warnLow = range.warningLow
        dangerLow = range.dangerLow
        warnHigh = range.warningHigh
        dangerHigh = range.dangerHigh

        // Step 0: Type list
        populateTypeList()

        // Step 1: Face picker — set up on first show via renderStep
        // Hide style pills (no longer needed)
        listOf(R.id.styleRing, R.id.styleArc, R.id.styleBar,
               R.id.styleDigital, R.id.styleDual, R.id.styleSegment,
               R.id.styleCustom).forEach { id ->
            findViewById<View>(id)?.visibility = View.GONE
        }

        // Step 2: Unit cycling
        unitSelector.setOnClickListener {
            val w = WidgetType.entries[widgetIndex]
            val options = UnitOption.optionsFor(w)
            if (options.size > 1) {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                unitIndex = (unitIndex + 1) % options.size
                updatePreview()
            }
        }

        // Step 3: Limit toggles
        setupLimitToggle(limitWarnLowToggle, limitWarnLowInput) { warnLow = null }
        setupLimitToggle(limitDangerLowToggle, limitDangerLowInput) { dangerLow = null }
        setupLimitToggle(limitWarnHighToggle, limitWarnHighInput) { warnHigh = null }
        setupLimitToggle(limitDangerHighToggle, limitDangerHighInput) { dangerHigh = null }

        // Navigation
        backButton.setOnClickListener {
            it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
            if (step == 0) {
                setResult(Activity.RESULT_CANCELED)
                finish()
            } else {
                if (step == 3) readRangeFields()
                step--
                renderStep()
            }
        }
        nextButton.setOnClickListener {
            it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
            if (step == totalSteps - 1) {
                readRangeFields()
                save()
            } else {
                if (step == 3) readRangeFields()
                step++
                if (step == 1) populateFacePicker()
                if (step == 3) populateRangeFields()
                renderStep()
            }
        }

        updatePreview()
        renderStep()
    }

    // ──────── Type list (step 0) ────────

    private fun populateTypeList() {
        typeList.removeAllViews()
        typeViewTags.clear()

        WidgetType.entries.forEachIndexed { index, widget ->
            val accent = ContextCompat.getColor(this, widget.accentColorRes)
            val row = LinearLayout(this).apply {
                orientation = LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER_VERTICAL
                layoutParams = LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, dp(42)
                ).apply { bottomMargin = dp(4) }
                setBackgroundResource(R.drawable.bg_vehicle_option)
                setPadding(dp(12), 0, dp(12), 0)
            }

            val dot = View(this).apply {
                layoutParams = LinearLayout.LayoutParams(dp(8), dp(8)).apply {
                    marginEnd = dp(10)
                }
                setBackgroundResource(R.drawable.bg_dot_indicator_active)
                background.setTint(accent)
            }
            row.addView(dot)

            val label = TextView(this).apply {
                text = widget.displayName
                textSize = 13f
                setTextColor(ContextCompat.getColor(context, R.color.watch_text))
                setTypeface(typeface, android.graphics.Typeface.BOLD)
                layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
            }
            row.addView(label)

            val unitHint = TextView(this).apply {
                text = widget.unit
                textSize = 10f
                setTextColor(ContextCompat.getColor(context, R.color.watch_muted))
            }
            row.addView(unitHint)

            row.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                widgetIndex = index
                unitIndex = 0
                hasPickedType = true
                selectedFaceId = FaceStore.defaultFaceFor(WidgetType.entries[widgetIndex])
                hasPickedFace = false
                resetRangeToDefaults()
                updateTypeSelection()
                updateNavButtons()
                updatePreview()
            }

            typeList.addView(row)
            typeViewTags.add(row to index)
        }

        // ── Multi-value bindings section ──
        addExtraBindingsSection()

        updateTypeSelection()
    }

    private fun updateTypeSelection() {
        typeViewTags.forEach { (view, idx) ->
            view.isSelected = hasPickedType && idx == widgetIndex
        }
    }

    // ──────── Extra Bindings UI (multi-value) ────────

    private fun addExtraBindingsSection() {
        if (!hasPickedType) return

        val primaryWidget = WidgetType.entries[widgetIndex]
        val accent = ContextCompat.getColor(this, primaryWidget.accentColorRes)

        // Section header
        val header = TextView(this).apply {
            text = "Données secondaires"
            textSize = 10f
            setTextColor(ContextCompat.getColor(context, R.color.watch_muted))
            layoutParams = LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
            ).apply { topMargin = dp(10); bottomMargin = dp(4) }
        }
        typeList.addView(header)

        // Existing bindings as chips
        for ((idx, binding) in extraBindings.withIndex()) {
            val bindAccent = ContextCompat.getColor(this, binding.widgetType.accentColorRes)
            val chipRow = LinearLayout(this).apply {
                orientation = LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER_VERTICAL
                layoutParams = LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, dp(34)
                ).apply { bottomMargin = dp(3) }
                setBackgroundResource(R.drawable.bg_watch_card)
                setPadding(dp(10), 0, dp(6), 0)
            }

            val dot = View(this).apply {
                layoutParams = LinearLayout.LayoutParams(dp(6), dp(6)).apply { marginEnd = dp(8) }
                setBackgroundResource(R.drawable.bg_dot_indicator_active)
                background.setTint(bindAccent)
            }
            chipRow.addView(dot)

            val label = TextView(this).apply {
                text = "${binding.widgetType.displayName} (${binding.unitOption.label})"
                textSize = 11f
                setTextColor(bindAccent)
                layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
            }
            chipRow.addView(label)

            val varHint = TextView(this).apply {
                text = "\$value-${binding.widgetType.key}"
                textSize = 8f
                setTextColor(ContextCompat.getColor(context, R.color.watch_muted))
                layoutParams = LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply { marginEnd = dp(6) }
            }
            chipRow.addView(varHint)

            val removeBtn = ImageView(this).apply {
                layoutParams = LinearLayout.LayoutParams(dp(20), dp(20))
                setImageResource(R.drawable.ic_close)
                setColorFilter(0xFFFF6B6B.toInt())
                scaleType = ImageView.ScaleType.FIT_CENTER
                val p = dp(2)
                setPadding(p, p, p, p)
            }
            removeBtn.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.REJECT)
                extraBindings.removeAt(idx)
                populateTypeList()
            }
            chipRow.addView(removeBtn)

            typeList.addView(chipRow)
        }

        // Add binding button
        val availableTypes = WidgetType.entries.filter { wt ->
            wt != primaryWidget && extraBindings.none { it.widgetType == wt }
        }
        if (availableTypes.isNotEmpty()) {
            val addBtn = LinearLayout(this).apply {
                orientation = LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER
                layoutParams = LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, dp(34)
                ).apply { topMargin = dp(2) }
                setBackgroundResource(R.drawable.bg_watch_card)
            }
            val addLabel = TextView(this).apply {
                text = "+ Ajouter une donnée"
                textSize = 10f
                setTextColor(accent)
            }
            addBtn.addView(addLabel)
            addBtn.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                showBindingPicker(availableTypes)
            }
            typeList.addView(addBtn)
        }
    }

    private fun showBindingPicker(availableTypes: List<WidgetType>) {
        val dialog = Dialog(this).apply {
            requestWindowFeature(android.view.Window.FEATURE_NO_TITLE)
            setCancelable(true)
            setCanceledOnTouchOutside(true)
        }

        val scroll = ScrollView(this).apply {
            setBackgroundResource(R.drawable.bg_watch_dialog)
            val p = dp(8)
            setPadding(p, p, p, p)
        }

        val list = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
        }

        val title = TextView(this).apply {
            text = "Donnée secondaire"
            textSize = 12f
            setTextColor(0xFFF1F5FF.toInt())
            gravity = Gravity.CENTER
            layoutParams = LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
            ).apply { bottomMargin = dp(8) }
        }
        list.addView(title)

        for (wt in availableTypes) {
            val accent = ContextCompat.getColor(this, wt.accentColorRes)
            val row = LinearLayout(this).apply {
                orientation = LinearLayout.HORIZONTAL
                gravity = Gravity.CENTER_VERTICAL
                layoutParams = LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, dp(38)
                ).apply { bottomMargin = dp(3) }
                setBackgroundResource(R.drawable.bg_vehicle_option)
                setPadding(dp(10), 0, dp(10), 0)
            }

            val dot = View(this).apply {
                layoutParams = LinearLayout.LayoutParams(dp(6), dp(6)).apply { marginEnd = dp(8) }
                setBackgroundResource(R.drawable.bg_dot_indicator_active)
                background.setTint(accent)
            }
            row.addView(dot)

            val label = TextView(this).apply {
                text = wt.displayName
                textSize = 12f
                setTextColor(ContextCompat.getColor(context, R.color.watch_text))
                layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
            }
            row.addView(label)

            val hint = TextView(this).apply {
                text = "\$value-${wt.key}"
                textSize = 8f
                setTextColor(ContextCompat.getColor(context, R.color.watch_muted))
            }
            row.addView(hint)

            row.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                extraBindings.add(DataBinding(wt))
                dialog.dismiss()
                populateTypeList()
            }

            list.addView(row)
        }

        scroll.addView(list)
        dialog.setContentView(scroll)
        dialog.window?.apply {
            setBackgroundDrawableResource(android.R.color.transparent)
            setLayout(dp(280), ViewGroup.LayoutParams.WRAP_CONTENT)
            setDimAmount(0.6f)
        }
        dialog.show()
    }

    // ──────── Steps ────────

    private fun renderStep() {
        dotViews.forEachIndexed { i, dot ->
            dot.setBackgroundResource(
                if (i == step) R.drawable.bg_dot_indicator_active
                else R.drawable.bg_dot_indicator
            )
        }
        stepViews.forEachIndexed { i, view ->
            if (i == step) {
                view.visibility = View.VISIBLE
                view.alpha = 0f
                view.translationX = dp(16).toFloat()
                view.animate().alpha(1f).translationX(0f).setDuration(200L).start()
            } else {
                view.animate().cancel()
                view.visibility = View.GONE
                view.alpha = 1f
                view.translationX = 0f
            }
        }

        // Step 1 (face picker): show full-screen face list
        if (step == 1) {
            for (i in 0 until stepStyle.childCount) {
                val child = stepStyle.getChildAt(i)
                if (child === facePickerScroll) {
                    child.visibility = View.VISIBLE
                } else {
                    child.visibility = View.GONE
                }
            }
            stepStyle.gravity = Gravity.TOP
        }

        backButton.setImageResource(if (step == 0) R.drawable.ic_close else R.drawable.ic_arrow_back)
        showButton(backButton, true)
        nextButton.setImageResource(
            if (step == totalSteps - 1) R.drawable.ic_check else R.drawable.ic_arrow_forward
        )
        updateNavButtons()
        updatePreview()
    }

    private fun canAdvance(): Boolean = when (step) {
        0 -> hasPickedType
        1 -> hasPickedFace
        2 -> true
        3 -> true
        else -> true
    }

    private fun updateNavButtons() {
        val enabled = canAdvance()
        showButton(nextButton, enabled)
    }

    private fun showButton(btn: View, show: Boolean) {
        val isVisible = btn.visibility == View.VISIBLE && btn.alpha > 0.5f
        if (show && !isVisible) {
            btn.visibility = View.VISIBLE
            btn.alpha = 0f
            btn.scaleX = 0.5f
            btn.scaleY = 0.5f
            btn.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(200L).start()
        } else if (!show && isVisible) {
            btn.animate().alpha(0f).scaleX(0.5f).scaleY(0.5f).setDuration(150L)
                .withEndAction { btn.visibility = View.GONE }.start()
        } else if (show) {
            btn.visibility = View.VISIBLE
            btn.alpha = 1f
            btn.scaleX = 1f
            btn.scaleY = 1f
        } else {
            btn.visibility = View.GONE
        }
    }

    private fun updatePreview() {
        val widget = WidgetType.entries[widgetIndex]
        val unitOptions = UnitOption.optionsFor(widget)
        val unitOption = unitOptions[unitIndex.coerceIn(0, unitOptions.lastIndex)]
        val accent = ContextCompat.getColor(this, widget.accentColorRes)

        typeName.text = widget.displayName
        typeName.setTextColor(accent)

        // Build limits from current range state
        val limits = GaugeLimits(warnLow, dangerLow, warnHigh, dangerHigh).convert(unitOption)
        val dispMin = unitOption.convert(rangeMin)
        val dispMax = unitOption.convert(rangeMax)
        val fakeValue = dispMin + (dispMax - dispMin) * 0.65

        for (preview in previews) {
            val face = selectedFaceId?.let { FaceStore.loadOrPreset(this, it) }
            preview.faceDef = face
            preview.formattedValue = formatField(fakeValue, unitOption)
            preview.unitLabel = unitOption.label
            preview.gaugeLabel = widget.displayName
            preview.setGauge(fakeValue, dispMin, dispMax, accent, limits)
        }

        // Unit selector
        if (unitOptions.size > 1) {
            unitSelector.visibility = View.VISIBLE
            unitSelector.text = "${unitOption.label}  ${unitIndex + 1}/${unitOptions.size}"
            unitSelector.setTextColor(accent)
        } else {
            unitSelector.visibility = View.VISIBLE
            unitSelector.text = unitOption.label
            unitSelector.setTextColor(ContextCompat.getColor(this, R.color.watch_muted))
        }
    }

    // ──────── Range/Limits ────────

    private fun resetRangeToDefaults() {
        val range = GaugeRange.defaultFor(WidgetType.entries[widgetIndex], vehicleProfile)
        rangeMin = range.minValue
        rangeMax = range.maxValue
        warnLow = range.warningLow
        dangerLow = range.dangerLow
        warnHigh = range.warningHigh
        dangerHigh = range.dangerHigh
    }

    private fun populateRangeFields() {
        val unitOption = currentUnitOption()

        rangeMinInput.setText(formatField(unitOption.convert(rangeMin), unitOption))
        rangeMaxInput.setText(formatField(unitOption.convert(rangeMax), unitOption))

        setLimitField(limitWarnLowInput, limitWarnLowToggle, warnLow, unitOption)
        setLimitField(limitDangerLowInput, limitDangerLowToggle, dangerLow, unitOption)
        setLimitField(limitWarnHighInput, limitWarnHighToggle, warnHigh, unitOption)
        setLimitField(limitDangerHighInput, limitDangerHighToggle, dangerHigh, unitOption)
    }

    private fun readRangeFields() {
        val unitOption = currentUnitOption()

        rangeMinInput.text?.toString()?.toDoubleOrNull()?.let { rangeMin = unitOption.toRaw(it) }
        rangeMaxInput.text?.toString()?.toDoubleOrNull()?.let { rangeMax = unitOption.toRaw(it) }

        warnLow = readLimitField(limitWarnLowInput, limitWarnLowToggle, unitOption)
        dangerLow = readLimitField(limitDangerLowInput, limitDangerLowToggle, unitOption)
        warnHigh = readLimitField(limitWarnHighInput, limitWarnHighToggle, unitOption)
        dangerHigh = readLimitField(limitDangerHighInput, limitDangerHighToggle, unitOption)
    }

    private fun setLimitField(input: EditText, toggle: ImageView, rawValue: Double?, unitOption: UnitOption) {
        if (rawValue != null) {
            input.setText(formatField(unitOption.convert(rawValue), unitOption))
            input.alpha = 1f
            input.isEnabled = true
            toggle.setImageResource(R.drawable.ic_close)
            toggle.tag = true
        } else {
            input.setText("")
            input.alpha = 0.3f
            input.isEnabled = false
            toggle.setImageResource(R.drawable.ic_add)
            toggle.tag = false
        }
    }

    private fun readLimitField(input: EditText, toggle: ImageView, unitOption: UnitOption): Double? {
        if (toggle.tag != true) return null
        return input.text?.toString()?.toDoubleOrNull()?.let { unitOption.toRaw(it) }
    }

    private fun setupLimitToggle(toggle: ImageView, input: EditText, onDisable: () -> Unit) {
        toggle.setOnClickListener {
            it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
            val enabled = toggle.tag == true
            if (enabled) {
                onDisable()
                input.setText("")
                input.alpha = 0.3f
                input.isEnabled = false
                toggle.setImageResource(R.drawable.ic_add)
                toggle.tag = false
            } else {
                input.setText("0")
                input.alpha = 1f
                input.isEnabled = true
                input.requestFocus()
                toggle.setImageResource(R.drawable.ic_close)
                toggle.tag = true
            }
        }
    }

    private fun formatField(value: Double, unitOption: UnitOption): String {
        return if (unitOption.decimals == 0) {
            value.toInt().toString()
        } else {
            "%.${unitOption.decimals}f".format(value)
        }
    }

    private fun currentUnitOption(): UnitOption {
        val widget = WidgetType.entries[widgetIndex]
        val options = UnitOption.optionsFor(widget)
        return options[unitIndex.coerceIn(0, options.lastIndex)]
    }

    // ──────── Save ────────

    private fun save() {
        val widget = WidgetType.entries[widgetIndex]
        val unitOption = currentUnitOption()
        val range = GaugeRange(rangeMin, rangeMax, warnLow, dangerLow, warnHigh, dangerHigh)

        val result = Intent().apply {
            putExtra(EXTRA_WIDGET_KEY, widget.key)
            putExtra(EXTRA_FACE_ID, selectedFaceId)
            putExtra(EXTRA_UNIT_KEY, unitOption.key)
            putExtra(EXTRA_RANGE, range.serialize())
            putExtra(EXTRA_PAGE_INDEX, pageIndex)
            putExtra(EXTRA_CELL_INDEX, cellIndex)
            if (extraBindings.isNotEmpty()) {
                putExtra(EXTRA_BINDINGS, extraBindings.joinToString(";") { it.serialize() })
            }
        }
        setResult(Activity.RESULT_OK, result)
        finish()
    }

    // ──────── Face Picker ────────

    private fun populateFacePicker() {
        facePickerList.removeAllViews()
        val accent = ContextCompat.getColor(this, WidgetType.entries[widgetIndex].accentColorRes)

        // ── Category filter chips ──
        val filterScroll = android.widget.HorizontalScrollView(this).apply {
            isHorizontalScrollBarEnabled = false
            layoutParams = LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
            ).apply { bottomMargin = dp(6) }
        }
        val filterRow = LinearLayout(this).apply {
            orientation = LinearLayout.HORIZONTAL
            val p = dp(2)
            setPadding(p, 0, p, 0)
        }
        for (cat in FaceStore.FaceCategory.entries) {
            val chip = TextView(this).apply {
                text = cat.label
                textSize = 9f
                gravity = Gravity.CENTER
                val pH = dp(10)
                val pV = dp(5)
                setPadding(pH, pV, pH, pV)
                layoutParams = LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply { marginEnd = dp(4) }
                setBackgroundResource(R.drawable.bg_watch_chip)
                if (cat == selectedCategory) {
                    setTextColor(accent)
                    setTypeface(null, Typeface.BOLD)
                } else {
                    setTextColor(ContextCompat.getColor(this@GaugeConfigActivity, R.color.watch_muted))
                }
                setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    selectedCategory = cat
                    populateFacePicker()
                }
            }
            filterRow.addView(chip)
        }
        filterScroll.addView(filterRow)
        facePickerList.addView(filterScroll)

        // ── Build entries list ──
        data class FaceEntry(val id: String, val name: String, val isPreset: Boolean)
        val entries = mutableListOf<FaceEntry>()

        for (key in FaceStore.PRESETS.keys) {
            if (selectedCategory != FaceStore.FaceCategory.ALL &&
                selectedCategory !in FaceStore.categoriesFor(key)) continue
            val face = try { FaceParser.parse(FaceStore.PRESETS[key]!!) } catch (_: Exception) { continue }
            entries.add(FaceEntry(key, face.name, true))
        }
        for (id in FaceStore.listIds(this)) {
            val face = FaceStore.load(this, id) ?: continue
            entries.add(FaceEntry(id, face.name, false))
        }

        val colGap = dp(4)

        for (i in entries.indices step 2) {
            val row = LinearLayout(this).apply {
                orientation = LinearLayout.HORIZONTAL
                layoutParams = LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
                ).apply { bottomMargin = dp(4) }
            }

            for (col in 0..1) {
                val idx = i + col
                if (idx >= entries.size) {
                    row.addView(View(this).apply {
                        layoutParams = LinearLayout.LayoutParams(0, dp(1), 1f)
                    })
                    continue
                }

                val entry = entries[idx]
                val isSelected = hasPickedFace && entry.id == selectedFaceId

                val cell = FrameLayout(this).apply {
                    layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f).apply {
                        if (col == 0) marginEnd = colGap / 2 else marginStart = colGap / 2
                    }
                    setBackgroundResource(R.drawable.bg_card_selectable)
                    this.isSelected = isSelected
                    val p = dp(6)
                    setPadding(p, p, p, p)
                }

                val inner = LinearLayout(this).apply {
                    orientation = LinearLayout.VERTICAL
                    gravity = Gravity.CENTER_HORIZONTAL
                    layoutParams = FrameLayout.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    )
                }

                val previewSize = dp(70)
                val miniGauge = GaugeView(this).apply {
                    layoutParams = LinearLayout.LayoutParams(previewSize, previewSize).apply {
                        gravity = Gravity.CENTER_HORIZONTAL
                    }
                }
                val face = try { FaceStore.loadOrPreset(this, entry.id) } catch (_: Exception) { null }
                if (face != null) {
                    miniGauge.faceDef = face
                    miniGauge.formattedValue = "65"
                    miniGauge.unitLabel = "\u00B0C"
                    miniGauge.gaugeLabel = "Huile"
                    miniGauge.setGauge(65.0, 0.0, 100.0, accent, GaugeLimits())
                }
                inner.addView(miniGauge)

                val label = TextView(this).apply {
                    layoutParams = LinearLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    ).apply { topMargin = dp(3) }
                    text = entry.name
                    textSize = 10f
                    gravity = Gravity.CENTER
                    maxLines = 1
                    setTextColor(ContextCompat.getColor(this@GaugeConfigActivity, R.color.watch_text))
                    if (isSelected) setTypeface(null, Typeface.BOLD)
                }
                inner.addView(label)
                cell.addView(inner)

                if (!entry.isPreset) {
                    val delBtn = ImageView(this).apply {
                        layoutParams = FrameLayout.LayoutParams(dp(22), dp(22)).apply {
                            gravity = Gravity.TOP or Gravity.END
                        }
                        setImageResource(R.drawable.ic_delete)
                        setColorFilter(0xFFFF6B6B.toInt())
                        scaleType = ImageView.ScaleType.FIT_CENTER
                        val p2 = dp(3)
                        setPadding(p2, p2, p2, p2)
                    }
                    delBtn.setOnClickListener {
                        it.performHapticFeedback(HapticFeedbackConstants.REJECT)
                        FaceStore.delete(this, entry.id)
                        if (selectedFaceId == entry.id) {
                            selectedFaceId = FaceStore.PRESETS.keys.firstOrNull()
                        }
                        populateFacePicker()
                        updatePreview()
                    }
                    cell.addView(delBtn)
                }

                cell.setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    selectedFaceId = entry.id
                    hasPickedFace = true
                    populateFacePicker()
                    updateNavButtons()
                    updatePreview()
                }

                row.addView(cell)
            }

            facePickerList.addView(row)
        }

        // Import buttons
        fun makeImportRow(label: String, onClick: () -> Unit) = LinearLayout(this).apply {
            orientation = LinearLayout.HORIZONTAL
            gravity = Gravity.CENTER
            layoutParams = LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, dp(38)
            ).apply { topMargin = dp(4) }
            setBackgroundResource(R.drawable.bg_watch_card)
            addView(TextView(context).apply {
                text = label; textSize = 10f; setTextColor(accent)
            })
            setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                onClick()
            }
        }
        facePickerList.addView(makeImportRow("+ Scanner un QR code") {
            qrScanLauncher.launch(Intent(this, QrScanActivity::class.java))
        })
        facePickerList.addView(makeImportRow("+ Coller depuis le presse-papiers") { importFaceFromClipboard() })
        facePickerList.addView(makeImportRow("+ Importer un fichier .face") { faceFilePicker.launch("*/*") })
    }

    private fun importFaceFromClipboard() {
        val clipboard = getSystemService(android.content.ClipboardManager::class.java)
        val text = clipboard?.primaryClip?.getItemAt(0)?.text?.toString()
        if (text.isNullOrBlank()) {
            Toast.makeText(this, "Presse-papiers vide", Toast.LENGTH_SHORT).show()
            return
        }
        importFaceFromText(text)
    }

    private fun importFaceFromText(text: String) {
        Log.d("FaceImport", "Text length: ${text.length}, first 200: ${text.take(200)}")
        val face = try {
            FaceParser.parse(text)
        } catch (e: Exception) {
            Log.e("FaceImport", "Parse failed", e)
            Toast.makeText(this, "Erreur de parsing: ${e.message}", Toast.LENGTH_LONG).show()
            return
        }
        val id = FaceStore.save(this, face)
        selectedFaceId = id
        hasPickedFace = true
        populateFacePicker()
        updateNavButtons()
        updatePreview()
        Toast.makeText(this, "Cadran \"${face.name}\" importé", Toast.LENGTH_SHORT).show()
    }

    @Deprecated("Use OnBackPressedCallback")
    override fun onBackPressed() {
        showCancelDialog()
    }

    private fun showCancelDialog() {
        val dialog = Dialog(this).apply {
            requestWindowFeature(android.view.Window.FEATURE_NO_TITLE)
            setCancelable(true)
            setCanceledOnTouchOutside(true)
        }

        val root = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
            setBackgroundResource(R.drawable.bg_watch_dialog)
            val p = dp(10)
            setPadding(p, dp(12), p, dp(12))
        }

        val title = TextView(this).apply {
            text = "Annuler les modifications ?"
            textSize = 12f
            setTextColor(0xFFF1F5FF.toInt())
            gravity = Gravity.CENTER
            layoutParams = LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
            ).apply { bottomMargin = dp(10) }
        }
        root.addView(title)

        val btnRow = LinearLayout(this).apply {
            orientation = LinearLayout.HORIZONTAL
            gravity = Gravity.CENTER
            layoutParams = LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
            )
        }

        val cancelBtn = TextView(this).apply {
            text = "Non"
            textSize = 12f
            setTextColor(0xFFAAAAAA.toInt())
            gravity = Gravity.CENTER
            setBackgroundResource(R.drawable.bg_settings_chip)
            layoutParams = LinearLayout.LayoutParams(0, dp(36), 1f).apply { marginEnd = dp(4) }
            setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss()
            }
        }
        btnRow.addView(cancelBtn)

        val confirmBtn = TextView(this).apply {
            text = "Oui"
            textSize = 12f
            setTextColor(0xFFFF6B6B.toInt())
            gravity = Gravity.CENTER
            setBackgroundResource(R.drawable.bg_settings_chip)
            layoutParams = LinearLayout.LayoutParams(0, dp(36), 1f).apply { marginStart = dp(4) }
            setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                dialog.dismiss()
                setResult(Activity.RESULT_CANCELED)
                finish()
            }
        }
        btnRow.addView(confirmBtn)
        root.addView(btnRow)

        dialog.setContentView(root)
        dialog.window?.apply {
            setBackgroundDrawableResource(android.R.color.transparent)
            setLayout(dp(260), ViewGroup.LayoutParams.WRAP_CONTENT)
            setDimAmount(0.6f)
        }
        dialog.show()
    }

    companion object {
        const val EXTRA_PAGE_INDEX = "page_index"
        const val EXTRA_CELL_INDEX = "cell_index"
        const val EXTRA_WIDGET_KEY = "widget_key"
        const val EXTRA_UNIT_KEY = "unit_key"
        const val EXTRA_RANGE = "range"
        const val EXTRA_FACE_ID = "face_id"
        const val EXTRA_BINDINGS = "extra_bindings"
    }
}
