package com.tt.honeyDue.ui.haptics

import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.view.HapticFeedbackConstants
import android.view.View

/**
 * Android backend using [HapticFeedbackConstants] when a host [View] is available,
 * with graceful [Vibrator] fallback for older APIs or headless contexts.
 *
 * API-30+ (Android 11+) gets the richer CONFIRM / REJECT / GESTURE_END constants.
 * Pre-30 falls back to predefined [VibrationEffect]s (EFFECT_TICK, EFFECT_CLICK,
 * EFFECT_HEAVY_CLICK) on API 29+, or one-shot vibrations on API 26–28,
 * or legacy Vibrator.vibrate(duration) on pre-26.
 *
 * Call [HapticsInit.install] from your Application / MainActivity so the app
 * context is available for vibrator resolution. Without it, the backend is
 * silently a no-op (never crashes).
 */
class AndroidDefaultHapticBackend(
    private val viewProvider: () -> View? = { null },
    private val vibratorProvider: () -> Vibrator? = { null }
) : HapticBackend {

    override fun perform(event: HapticEvent) {
        val view = viewProvider()
        if (view != null && performViaView(view, event)) return
        performViaVibrator(event)
    }

    private fun performViaView(view: View, event: HapticEvent): Boolean {
        val constant = when (event) {
            HapticEvent.LIGHT -> HapticFeedbackConstants.CONTEXT_CLICK
            HapticEvent.MEDIUM -> HapticFeedbackConstants.KEYBOARD_TAP
            HapticEvent.HEAVY -> HapticFeedbackConstants.LONG_PRESS
            HapticEvent.SUCCESS -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                HapticFeedbackConstants.CONFIRM
            } else {
                HapticFeedbackConstants.CONTEXT_CLICK
            }
            HapticEvent.WARNING -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                HapticFeedbackConstants.GESTURE_END
            } else {
                HapticFeedbackConstants.LONG_PRESS
            }
            HapticEvent.ERROR -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                HapticFeedbackConstants.REJECT
            } else {
                HapticFeedbackConstants.LONG_PRESS
            }
        }
        return view.performHapticFeedback(constant)
    }

    @Suppress("DEPRECATION")
    private fun performViaVibrator(event: HapticEvent) {
        val vibrator = vibratorProvider() ?: return
        if (!vibrator.hasVibrator()) return

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val predefined = when (event) {
                HapticEvent.LIGHT, HapticEvent.SUCCESS -> VibrationEffect.EFFECT_TICK
                HapticEvent.MEDIUM, HapticEvent.WARNING -> VibrationEffect.EFFECT_CLICK
                HapticEvent.HEAVY, HapticEvent.ERROR -> VibrationEffect.EFFECT_HEAVY_CLICK
            }
            vibrator.vibrate(VibrationEffect.createPredefined(predefined))
            return
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val (duration, amplitude) = when (event) {
                HapticEvent.LIGHT -> 10L to VibrationEffect.DEFAULT_AMPLITUDE
                HapticEvent.MEDIUM -> 20L to VibrationEffect.DEFAULT_AMPLITUDE
                HapticEvent.HEAVY -> 50L to VibrationEffect.DEFAULT_AMPLITUDE
                HapticEvent.SUCCESS -> 30L to VibrationEffect.DEFAULT_AMPLITUDE
                HapticEvent.WARNING -> 40L to VibrationEffect.DEFAULT_AMPLITUDE
                HapticEvent.ERROR -> 60L to VibrationEffect.DEFAULT_AMPLITUDE
            }
            vibrator.vibrate(VibrationEffect.createOneShot(duration, amplitude))
            return
        }

        val duration = when (event) {
            HapticEvent.LIGHT -> 10L
            HapticEvent.MEDIUM -> 20L
            HapticEvent.HEAVY -> 50L
            HapticEvent.SUCCESS -> 30L
            HapticEvent.WARNING -> 40L
            HapticEvent.ERROR -> 60L
        }
        vibrator.vibrate(duration)
    }
}

/**
 * Android app-wide registry that plumbs an Application Context to the default
 * backend. Call [HapticsInit.install] from the Application or Activity init so
 * that call-sites in shared code can invoke [Haptics.light] etc. without any
 * Compose / View plumbing.
 */
object HapticsInit {
    @Volatile private var appContext: Context? = null
    @Volatile private var hostView: View? = null

    fun install(context: Context) {
        appContext = context.applicationContext
    }

    fun attachView(view: View?) {
        hostView = view
    }

    internal fun defaultBackend(): HapticBackend = AndroidDefaultHapticBackend(
        viewProvider = { hostView },
        vibratorProvider = { resolveVibrator() }
    )

    @Suppress("DEPRECATION")
    private fun resolveVibrator(): Vibrator? {
        val ctx = appContext ?: return null
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            (ctx.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as? VibratorManager)?.defaultVibrator
        } else {
            ctx.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
        }
    }
}

actual object Haptics {
    @Volatile private var backend: HapticBackend = HapticsInit.defaultBackend()

    actual fun light() = backend.perform(HapticEvent.LIGHT)
    actual fun medium() = backend.perform(HapticEvent.MEDIUM)
    actual fun heavy() = backend.perform(HapticEvent.HEAVY)
    actual fun success() = backend.perform(HapticEvent.SUCCESS)
    actual fun warning() = backend.perform(HapticEvent.WARNING)
    actual fun error() = backend.perform(HapticEvent.ERROR)

    actual fun setBackend(backend: HapticBackend) {
        this.backend = backend
    }

    actual fun resetBackend() {
        this.backend = HapticsInit.defaultBackend()
    }
}
