package dev.kaizen.app.ui.sensor

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.PowerManager
import android.provider.Settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver

/**
 * Provides a smoothed tilt [Offset] (x, y in range -1..1) from the device sensors.
 *
 * Uses [Sensor.TYPE_GAME_ROTATION_VECTOR] (fused gyro + accel, pre-stabilized,
 * no drift). Falls back to [Sensor.TYPE_GRAVITY] then [Sensor.TYPE_ACCELEROMETER].
 *
 * Automatically disables when:
 * - System animations are off (`ANIMATOR_DURATION_SCALE == 0`)
 * - Battery saver is on
 * - App is not in foreground
 * - Device tilt exceeds ±25° (user lying down)
 *
 * See DESIGN.md §7 for full motion spec.
 */
@Composable
fun rememberTiltState(): State<Offset> {
    val context = LocalContext.current
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    val tilt = remember { mutableStateOf(Offset.Zero) }

    DisposableEffect(lifecycle) {
        val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
            ?: return@DisposableEffect onDispose { }

        val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
            ?: sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
            ?: sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
            ?: return@DisposableEffect onDispose { }

        val isRotationVector = sensor.type == Sensor.TYPE_GAME_ROTATION_VECTOR

        val animScale = try {
            Settings.Global.getFloat(context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE)
        } catch (_: Exception) { 1f }

        if (animScale == 0f) return@DisposableEffect onDispose { }

        var filteredX = 0f
        var filteredY = 0f
        val alpha = 0.18f
        var registered = false

        val listener = object : SensorEventListener {
            override fun onSensorChanged(event: SensorEvent?) {
                if (event == null) return

                val pm = context.getSystemService(Context.POWER_SERVICE) as? PowerManager
                if (pm?.isPowerSaveMode == true) {
                    tilt.value = Offset.Zero
                    return
                }

                val rawX: Float
                val rawY: Float

                if (isRotationVector) {
                    val rotationMatrix = FloatArray(9)
                    SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
                    val orientation = FloatArray(3)
                    SensorManager.getOrientation(rotationMatrix, orientation)
                    val pitch = Math.toDegrees(orientation[1].toDouble()).toFloat()
                    val roll = Math.toDegrees(orientation[2].toDouble()).toFloat()

                    if (kotlin.math.abs(pitch) > 25f || kotlin.math.abs(roll) > 25f) {
                        tilt.value = Offset.Zero
                        return
                    }

                    rawX = (roll / 25f).coerceIn(-1f, 1f)
                    rawY = (pitch / 25f).coerceIn(-1f, 1f)
                } else {
                    val x = event.values[0] / 9.81f
                    val y = event.values[1] / 9.81f

                    if (kotlin.math.abs(x) > 0.6f || kotlin.math.abs(y) > 0.6f) {
                        tilt.value = Offset.Zero
                        return
                    }

                    rawX = x.coerceIn(-1f, 1f)
                    rawY = y.coerceIn(-1f, 1f)
                }

                filteredX = filteredX + alpha * (rawX - filteredX)
                filteredY = filteredY + alpha * (rawY - filteredY)

                tilt.value = Offset(filteredX, filteredY)
            }

            override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
        }

        val lifecycleObserver = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_START -> {
                    if (!registered) {
                        sensorManager.registerListener(
                            listener, sensor, SensorManager.SENSOR_DELAY_UI
                        )
                        registered = true
                    }
                }
                Lifecycle.Event.ON_STOP -> {
                    sensorManager.unregisterListener(listener)
                    registered = false
                    tilt.value = Offset.Zero
                }
                else -> {}
            }
        }

        lifecycle.addObserver(lifecycleObserver)
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI)
            registered = true
        }

        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
            if (registered) sensorManager.unregisterListener(listener)
        }
    }

    return tilt
}
