package com.erwan.revwatch.ui

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Paint
import android.graphics.Typeface
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.erwan.revwatch.R
import com.erwan.revwatch.model.VehicleBrand
import com.erwan.revwatch.util.dp

class BrandSplashOverlay @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {

    private val letterContainer: LinearLayout
    private val accentLine: View
    private var currentAnim: ValueAnimator? = null
    private val pendingRunnables = mutableListOf<Runnable>()

    companion object {
        private const val MIN_DURATION_MS = 3000L
    }

    init {
        setBackgroundColor(ContextCompat.getColor(context, R.color.watch_black))
        isClickable = true
        isFocusable = true

        // Disable clipping so cursive letters can overflow
        clipChildren = false
        clipToPadding = false

        // Vertical wrapper to center letters + line
        val wrapper = LinearLayout(context).apply {
            orientation = LinearLayout.VERTICAL
            gravity = Gravity.CENTER_HORIZONTAL
            clipChildren = false
            clipToPadding = false
            layoutParams = LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER
            )
        }

        letterContainer = LinearLayout(context).apply {
            orientation = LinearLayout.HORIZONTAL
            clipChildren = false
            clipToPadding = false
            layoutParams = LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT
            )
        }

        accentLine = View(context).apply {
            layoutParams = LinearLayout.LayoutParams(context.dp(48), context.dp(2)).apply {
                topMargin = context.dp(12)
            }
        }

        wrapper.addView(letterContainer)
        wrapper.addView(accentLine)
        addView(wrapper)

        visibility = GONE
    }

    fun playIntro(brand: VehicleBrand, onDone: () -> Unit) {
        cancel()
        visibility = VISIBLE
        alpha = 1f

        val letters = setupLetters(brand)
        letters.forEach { it.alpha = 0f; it.translationY = context.dp(12).toFloat() }
        accentLine.scaleX = 0f
        accentLine.alpha = 0f

        val startTime = System.currentTimeMillis()
        val n = letters.size
        val decel = DecelerateInterpolator(2f)
        val smooth = AccelerateDecelerateInterpolator()
        val totalDuration = 2400L

        currentAnim = ValueAnimator.ofFloat(0f, 1f).apply {
            duration = totalDuration
            interpolator = null

            addUpdateListener { va ->
                val t = va.animatedValue as Float

                for (i in 0 until n) {
                    val ls = (i.toFloat() / n) * 0.55f
                    val le = ls + 0.30f
                    val p = decel.getInterpolation(((t - ls) / (le - ls)).coerceIn(0f, 1f))
                    letters[i].alpha = p
                    letters[i].translationY = context.dp(12) * (1f - p)
                    val s = 0.85f + 0.15f * p
                    letters[i].scaleX = s
                    letters[i].scaleY = s
                }

                val lineP = decel.getInterpolation(((t - 0.45f) / 0.20f).coerceIn(0f, 1f))
                accentLine.alpha = lineP
                accentLine.scaleX = lineP

                val pulseUpP = smooth.getInterpolation(((t - 0.65f) / 0.20f).coerceIn(0f, 1f))
                val pulseDownP = smooth.getInterpolation(((t - 0.85f) / 0.15f).coerceIn(0f, 1f))
                val pulse = 1f + 0.06f * pulseUpP - 0.06f * pulseDownP
                for (i in 0 until n) {
                    if (letters[i].alpha >= 0.99f) {
                        letters[i].scaleX = pulse
                        letters[i].scaleY = pulse
                    }
                }
                accentLine.scaleX = lineP * pulse
            }
        }
        currentAnim?.start()

        postTracked(totalDuration) {
            val elapsed = System.currentTimeMillis() - startTime
            val remaining = (MIN_DURATION_MS - elapsed).coerceAtLeast(0)
            postTracked(remaining) {
                animate().alpha(0f).setDuration(350L).withEndAction {
                    visibility = GONE
                    letterContainer.removeAllViews()
                    onDone()
                }.start()
            }
        }
    }

    fun playOutro(brand: VehicleBrand, onDone: () -> Unit) {
        cancel()
        visibility = VISIBLE
        alpha = 0f

        val letters = setupLetters(brand)
        letters.forEach { it.alpha = 0f }
        accentLine.scaleX = 1f
        accentLine.alpha = 0f

        val n = letters.size
        val decel = DecelerateInterpolator(2f)
        val accel = AccelerateInterpolator(2f)
        val totalDuration = 2800L

        currentAnim = ValueAnimator.ofFloat(0f, 1f).apply {
            duration = totalDuration
            interpolator = null

            addUpdateListener { va ->
                val t = va.animatedValue as Float

                // Overlay fades in (black covers dashboard)
                alpha = decel.getInterpolation((t / 0.25f).coerceIn(0f, 1f))

                // Letters + line fade in
                val letterIn = decel.getInterpolation(((t - 0.15f) / 0.25f).coerceIn(0f, 1f))
                for (i in 0 until n) { letters[i].alpha = letterIn }
                accentLine.alpha = letterIn

                // Letters + line fade out
                val letterOutRaw = ((t - 0.60f) / 0.30f).coerceIn(0f, 1f)
                val letterOut = accel.getInterpolation(letterOutRaw)
                if (letterOutRaw > 0f) {
                    val a = letterIn * (1f - letterOut)
                    for (i in 0 until n) { letters[i].alpha = a }
                    accentLine.alpha = a
                    accentLine.scaleX = 1f - letterOut * 0.5f
                }
            }
        }
        currentAnim?.start()

        postTracked(totalDuration + 200L) {
            letterContainer.removeAllViews()
            onDone()
        }
    }

    fun cancel() {
        currentAnim?.cancel()
        currentAnim = null
        animate().cancel()
        // Only remove our own callbacks, not ALL main thread callbacks
        for (r in pendingRunnables) {
            removeCallbacks(r)
        }
        pendingRunnables.clear()
    }

    private fun postTracked(delayMs: Long, block: () -> Unit) {
        val r = Runnable { block() }
        pendingRunnables.add(r)
        postDelayed(r, delayMs)
    }

    private fun setupLetters(brand: VehicleBrand): List<TextView> {
        letterContainer.removeAllViews()

        val typeface = loadBrandFont(brand)
        val brandName = brand.displayName
        val color = ContextCompat.getColor(context, R.color.watch_text)

        val marginPx = context.dp(48)
        val availableWidth = resources.displayMetrics.widthPixels - marginPx * 2
        val baseSize = if (brand == VehicleBrand.FORD) 64f else 42f
        val textSizeSp = fitTextSize(brandName, typeface, baseSize, availableWidth.toFloat())

        val accentColor = when (brand) {
            VehicleBrand.FORD -> R.color.watch_accent
            VehicleBrand.HONDA -> R.color.watch_error
            else -> R.color.watch_accent
        }
        accentLine.setBackgroundColor(ContextCompat.getColor(context, accentColor))

        // Negative margin for cursive fonts so letters overlap naturally
        val overlapPx = if (brand == VehicleBrand.FORD) context.dp(-4) else 0

        return brandName.mapIndexed { index, char ->
            TextView(context).apply {
                text = char.toString()
                setTextColor(color)
                this.textSize = textSizeSp
                this.typeface = typeface
                gravity = Gravity.CENTER
                includeFontPadding = false
                setLayerType(LAYER_TYPE_HARDWARE, null)
                layoutParams = LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.WRAP_CONTENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT
                ).apply {
                    if (index > 0 && overlapPx != 0) marginStart = overlapPx
                }
            }.also { letterContainer.addView(it) }
        }
    }

    private fun loadBrandFont(brand: VehicleBrand): Typeface {
        val fontPath = when (brand) {
            VehicleBrand.FORD -> "fonts/fordscript.ttf"
            VehicleBrand.HONDA -> "fonts/hondafont.ttf"
            else -> null
        }
        if (fontPath == null) return Typeface.DEFAULT_BOLD
        return try {
            Typeface.createFromAsset(context.assets, fontPath)
        } catch (_: Exception) {
            Typeface.DEFAULT_BOLD
        }
    }

    private fun fitTextSize(text: String, typeface: Typeface, maxSp: Float, maxWidthPx: Float): Float {
        val density = resources.displayMetrics.scaledDensity
        val paint = Paint().apply {
            this.typeface = typeface
            this.textSize = maxSp * density
        }
        val measured = paint.measureText(text)
        return if (measured > maxWidthPx) maxSp * (maxWidthPx / measured) else maxSp
    }

}
