package com.erwan.revwatch.ui

import android.content.Context
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.erwan.revwatch.R
import com.erwan.revwatch.model.CellConfig
import com.erwan.revwatch.model.DataBinding
import com.erwan.revwatch.model.DashPage
import com.erwan.revwatch.model.GaugeLimits
import com.erwan.revwatch.model.MetricSnapshot
import com.erwan.revwatch.model.UnitOption
import com.erwan.revwatch.model.face.FaceStore
import com.erwan.revwatch.util.dp

class GridPagerAdapter(
    private val onCellTap: (pageIndex: Int, cellIndex: Int, existing: CellConfig?) -> Unit,
    private val onCellDelete: (pageIndex: Int, cellIndex: Int) -> Unit,
    private val onAddPage: () -> Unit,
    private val onLongPress: () -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    companion object {
        private const val TYPE_GRID = 0
        private const val TYPE_ADD_PAGE = 1
        const val PAYLOAD_SNAPSHOT = "snapshot"
        private const val PAYLOAD_EDIT_MODE = "edit_mode"
    }

    private var pages: List<DashPage> = emptyList()
    private var snapshot = MetricSnapshot()
    var editMode = false
        private set

    override fun getItemCount(): Int = pages.size + if (editMode) 1 else 0

    override fun getItemViewType(position: Int): Int =
        if (position >= pages.size) TYPE_ADD_PAGE else TYPE_GRID

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val ctx = parent.context
        return if (viewType == TYPE_ADD_PAGE) {
            AddPageViewHolder(createAddPageView(ctx))
        } else {
            GridViewHolder(FrameLayout(ctx).apply {
                layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
            })
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is GridViewHolder -> holder.bind(
                pages[position], position, snapshot, editMode,
                onCellTap, onCellDelete, onLongPress
            )
            is AddPageViewHolder -> holder.bind(onAddPage)
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any>) {
        if (payloads.contains(PAYLOAD_SNAPSHOT) && holder is GridViewHolder) {
            holder.updateValues(pages[position], snapshot)
        } else {
            super.onBindViewHolder(holder, position, payloads)
        }
    }

    fun updatePages(newPages: List<DashPage>) {
        pages = newPages
        notifyDataSetChanged()
    }

    fun updateSnapshot(value: MetricSnapshot) {
        snapshot = value
        if (pages.isNotEmpty()) {
            notifyItemRangeChanged(0, pages.size, PAYLOAD_SNAPSHOT)
        }
    }

    fun setEditMode(enabled: Boolean) {
        if (editMode == enabled) return
        editMode = enabled
        notifyDataSetChanged()
    }

    // ──────── Add Page View ────────

    private fun createAddPageView(ctx: Context): View {
        val frame = FrameLayout(ctx).apply {
            layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
            setBackgroundColor(ContextCompat.getColor(ctx, R.color.watch_black))
        }
        val icon = ImageView(ctx).apply {
            layoutParams = FrameLayout.LayoutParams(ctx.dp(48), ctx.dp(48)).apply {
                gravity = Gravity.CENTER
            }
            setImageResource(R.drawable.ic_add)
            setBackgroundResource(R.drawable.bg_nav_button)
            scaleType = ImageView.ScaleType.FIT_CENTER
            val p = ctx.dp(12)
            setPadding(p, p, p, p)
        }
        frame.addView(icon)
        val label = TextView(ctx).apply {
            layoutParams = FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
            ).apply {
                gravity = Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
                bottomMargin = ctx.dp(60)
            }
            text = "Nouvelle page"
            setTextColor(ContextCompat.getColor(ctx, R.color.watch_muted))
            textSize = 10f
        }
        frame.addView(label)
        return frame
    }

    class AddPageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        fun bind(onAdd: () -> Unit) {
            itemView.setOnClickListener {
                it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                onAdd()
            }
        }
    }

    // ──────── Grid Page View ────────

    class GridViewHolder(private val container: FrameLayout) : RecyclerView.ViewHolder(container) {

        private data class CellViews(
            val config: CellConfig,
            val gauge: GaugeView
        )
        private var cellViewsList: List<CellViews?> = emptyList()

        fun bind(
            page: DashPage, pageIndex: Int, snapshot: MetricSnapshot, editMode: Boolean,
            onCellTap: (Int, Int, CellConfig?) -> Unit,
            onCellDelete: (Int, Int) -> Unit,
            onLongPress: () -> Unit
        ) {
            container.removeAllViews()
            cellViewsList = emptyList()
            val ctx = container.context

            val grid = LinearLayout(ctx).apply {
                orientation = LinearLayout.VERTICAL
                layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
            }

            val cellGap = if (editMode) ctx.dp(3) else 0
            val cellRefs = mutableListOf<CellViews?>()

            for (row in 0 until page.rows) {
                val rowView = LinearLayout(ctx).apply {
                    orientation = LinearLayout.HORIZONTAL
                    layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, 0, 1f).apply {
                        if (editMode && row > 0) topMargin = cellGap
                    }
                }

                for (col in 0 until page.columns) {
                    val cellIndex = row * page.columns + col
                    val cell = page.cells.getOrNull(cellIndex)

                    val cellView: View
                    if (editMode) {
                        cellView = createEditCell(ctx, cell, pageIndex, cellIndex, onCellTap, onCellDelete)
                        cellRefs.add(null)
                    } else {
                        val refs = createNormalCell(ctx, cell, snapshot)
                        cellView = refs.first
                        cellRefs.add(refs.second)
                    }

                    cellView.layoutParams = LinearLayout.LayoutParams(0, MATCH_PARENT, 1f).apply {
                        if (editMode && col > 0) marginStart = cellGap
                    }
                    rowView.addView(cellView)
                }
                grid.addView(rowView)
            }

            cellViewsList = cellRefs
            container.addView(grid)

            // Show hint on empty pages (no gauges) in normal mode
            val allEmpty = page.cells.all { it == null }
            if (!editMode && allEmpty) {
                val hint = TextView(ctx).apply {
                    layoutParams = FrameLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    ).apply { gravity = Gravity.CENTER }
                    text = "Rester appuye\npour modifier la page"
                    setTextColor(ContextCompat.getColor(ctx, R.color.watch_muted))
                    textSize = 11f
                    textAlignment = View.TEXT_ALIGNMENT_CENTER
                }
                container.addView(hint)
            }

            if (!editMode) {
                container.setOnLongClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
                    onLongPress()
                    true
                }
            } else {
                container.setOnLongClickListener(null)
                container.isLongClickable = false
            }
        }

        fun updateValues(@Suppress("UNUSED_PARAMETER") page: DashPage, snapshot: MetricSnapshot) {
            for (i in cellViewsList.indices) {
                val refs = cellViewsList[i] ?: continue
                val cell = refs.config
                val unitOption = cell.unitOption
                val range = cell.resolvedRange
                val limits = range.toLimits().convert(unitOption)
                val rawValue = cell.widgetType.readFrom(snapshot)
                val value = rawValue?.let { unitOption.convert(it) }
                val accent = ContextCompat.getColor(container.context, cell.widgetType.accentColorRes)

                refs.gauge.formattedValue = formatValue(value, unitOption)
                refs.gauge.unitLabel = unitOption.label
                refs.gauge.gaugeLabel = cell.widgetType.displayName
                refs.gauge.extraVars = buildExtraVars(cell, snapshot)
                refs.gauge.setGauge(
                    value,
                    unitOption.convert(range.minValue), unitOption.convert(range.maxValue),
                    accent, limits
                )
            }
        }

        private fun createNormalCell(
            ctx: Context, cell: CellConfig?, snapshot: MetricSnapshot
        ): Pair<View, CellViews?> {
            val frame = FrameLayout(ctx)

            if (cell == null) return frame to null

            val unitOption = cell.unitOption
            val range = cell.resolvedRange
            val limits = range.toLimits().convert(unitOption)
            val rawValue = cell.widgetType.readFrom(snapshot)
            val value = rawValue?.let { unitOption.convert(it) }
            val accent = ContextCompat.getColor(ctx, cell.widgetType.accentColorRes)

            val gauge = GaugeView(ctx).apply {
                layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
            }
            // Load face
            val face = FaceStore.loadOrPreset(ctx, cell.faceId)
            gauge.faceDef = face
            gauge.formattedValue = formatValue(value, unitOption)
            gauge.unitLabel = unitOption.label
            gauge.gaugeLabel = cell.widgetType.displayName
            gauge.extraVars = buildExtraVars(cell, snapshot)
            gauge.setGauge(
                value,
                unitOption.convert(range.minValue), unitOption.convert(range.maxValue),
                accent, limits
            )
            frame.addView(gauge)

            return frame to CellViews(cell, gauge)
        }

        private fun createEditCell(
            ctx: Context, cell: CellConfig?, pageIndex: Int, cellIndex: Int,
            onCellTap: (Int, Int, CellConfig?) -> Unit,
            onCellDelete: (Int, Int) -> Unit
        ): View {
            val frame = FrameLayout(ctx).apply {
                setBackgroundResource(R.drawable.bg_watch_card)
                val p = ctx.dp(4)
                setPadding(p, p, p, p)
                clipToPadding = false
            }

            if (cell != null) {
                val gauge = GaugeView(ctx).apply {
                    layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
                }
                val unitOption = cell.unitOption
                val range = cell.resolvedRange
                val limits = range.toLimits().convert(unitOption)
                val accent = ContextCompat.getColor(ctx, cell.widgetType.accentColorRes)
                val face = FaceStore.loadOrPreset(ctx, cell.faceId)
                gauge.faceDef = face
                gauge.formattedValue = "--"
                gauge.unitLabel = unitOption.label
                gauge.gaugeLabel = cell.widgetType.displayName
                gauge.setGauge(
                    null,
                    unitOption.convert(range.minValue), unitOption.convert(range.maxValue),
                    accent, limits
                )
                frame.addView(gauge)

                val label = TextView(ctx).apply {
                    text = cell.widgetType.displayName
                    textSize = 9f
                    setTextColor(ContextCompat.getColor(ctx, cell.widgetType.accentColorRes))
                    gravity = Gravity.CENTER
                    layoutParams = FrameLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    ).apply { gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL; bottomMargin = ctx.dp(6) }
                }
                frame.addView(label)

                val deleteBtn = ImageView(ctx).apply {
                    layoutParams = FrameLayout.LayoutParams(ctx.dp(22), ctx.dp(22)).apply {
                        gravity = Gravity.TOP or Gravity.END
                    }
                    setImageResource(R.drawable.ic_delete)
                    setColorFilter(0xFFFF6B6B.toInt())
                    scaleType = ImageView.ScaleType.FIT_CENTER
                    val p2 = ctx.dp(3)
                    setPadding(p2, p2, p2, p2)
                    setOnClickListener {
                        it.performHapticFeedback(HapticFeedbackConstants.REJECT)
                        onCellDelete(pageIndex, cellIndex)
                    }
                }
                frame.addView(deleteBtn)

                frame.setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    onCellTap(pageIndex, cellIndex, cell)
                }
            } else {
                val addIcon = ImageView(ctx).apply {
                    layoutParams = FrameLayout.LayoutParams(ctx.dp(28), ctx.dp(28)).apply {
                        gravity = Gravity.CENTER
                    }
                    setImageResource(R.drawable.ic_add)
                    scaleType = ImageView.ScaleType.FIT_CENTER
                    val p2 = ctx.dp(4)
                    setPadding(p2, p2, p2, p2)
                }
                frame.addView(addIcon)

                frame.setOnClickListener {
                    it.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
                    onCellTap(pageIndex, cellIndex, null)
                }
            }

            return frame
        }

        /**
         * Build DSL variables for extra data bindings.
         * Each binding injects: $value-{key}, $progress-{key}, $raw-{key},
         * $zone-{key}, $unit-{key}, $label-{key}, $accent-{key}, $min-{key}, $max-{key}
         */
        private fun buildExtraVars(cell: CellConfig, snapshot: MetricSnapshot): Map<String, String> {
            if (cell.extraBindings.isEmpty()) return emptyMap()
            val vars = mutableMapOf<String, String>()
            for ((index, binding) in cell.extraBindings.withIndex()) {
                val key = binding.widgetType.key
                val unit = binding.unitOption
                val range = binding.resolvedRange
                val limits = range.toLimits().convert(unit)
                val rawVal = binding.widgetType.readFrom(snapshot)
                val displayVal = rawVal?.let { unit.convert(it) }
                val dispMin = unit.convert(range.minValue)
                val dispMax = unit.convert(range.maxValue)
                val dispRange = dispMax - dispMin
                val progress = if (displayVal != null && dispRange > 0.0001)
                    ((displayVal - dispMin) / dispRange).coerceIn(0.0, 1.0) else 0.0
                val zone = if (displayVal != null) limits.zoneFor(displayVal) else GaugeLimits.Zone.NORMAL
                val accent = binding.widgetType.accentColorRes
                val accentHex = String.format("#FF%06X", 0xFFFFFF and ContextCompat.getColor(container.context, accent))
                val fmtValue = formatValue(displayVal, unit)
                val zoneName = zone.name.lowercase()

                // Named by type key: $value-oil_temp, $progress-rpm, etc.
                vars["\$value-$key"] = fmtValue
                vars["\$progress-$key"] = progress.toString()
                vars["\$raw-$key"] = (rawVal ?: 0.0).toString()
                vars["\$zone-$key"] = zoneName
                vars["\$unit-$key"] = unit.label
                vars["\$label-$key"] = binding.widgetType.displayName
                vars["\$accent-$key"] = accentHex
                vars["\$min-$key"] = dispMin.toString()
                vars["\$max-$key"] = dispMax.toString()

                // Indexed: $value-1, $progress-1, etc. (1-based)
                val idx = index + 1
                vars["\$value-$idx"] = fmtValue
                vars["\$progress-$idx"] = progress.toString()
                vars["\$raw-$idx"] = (rawVal ?: 0.0).toString()
                vars["\$zone-$idx"] = zoneName
                vars["\$unit-$idx"] = unit.label
                vars["\$label-$idx"] = binding.widgetType.displayName
                vars["\$accent-$idx"] = accentHex
                vars["\$min-$idx"] = dispMin.toString()
                vars["\$max-$idx"] = dispMax.toString()
            }
            return vars
        }

        private val formatCache = HashMap<Int, String>(4)

        private fun formatValue(value: Double?, unitOption: com.erwan.revwatch.model.UnitOption): String {
            if (value == null) return "--"
            return if (unitOption.decimals == 0) value.toInt().toString()
            else {
                val fmt = formatCache.getOrPut(unitOption.decimals) { "%.${unitOption.decimals}f" }
                fmt.format(value)
            }
        }
    }

}
