package com.lazy.emulate.input

import android.content.Context
import android.hardware.input.InputManager
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class ControllerManager(
    context: Context,
    var buttonMappingManager: ButtonMappingManager? = null
) : InputManager.InputDeviceListener {

    private val inputManager = context.getSystemService(Context.INPUT_SERVICE) as InputManager

    private val _connectedControllers = MutableStateFlow<List<GameController>>(emptyList())
    val connectedControllers: StateFlow<List<GameController>> = _connectedControllers.asStateFlow()

    private val _activeController = MutableStateFlow<GameController?>(null)
    val activeController: StateFlow<GameController?> = _activeController.asStateFlow()

    var onButtonEvent: ((GamepadButton, Boolean) -> Unit)? = null
    var onAnalogEvent: ((Float, Float, Int) -> Unit)? = null

    fun start() {
        inputManager.registerInputDeviceListener(this, null)
        refreshControllers()
    }

    fun stop() {
        inputManager.unregisterInputDeviceListener(this)
    }

    private fun refreshControllers() {
        val controllers = InputDevice.getDeviceIds()
            .toList()
            .mapNotNull { InputDevice.getDevice(it) }
            .filter { isGameController(it) }
            .map { device ->
                GameController(
                    deviceId = device.id,
                    name = device.name,
                    vendorId = device.vendorId,
                    productId = device.productId,
                    isExternal = device.isExternal
                )
            }
        _connectedControllers.value = controllers
        if (_activeController.value == null && controllers.isNotEmpty()) {
            _activeController.value = controllers.first()
        }
    }

    fun handleKeyEvent(event: KeyEvent): Boolean {
        if (!isGameControllerDevice(event.device ?: return false)) return false

        val rawButton = mapKeyToButton(event.keyCode) ?: return false
        val button = buttonMappingManager?.remapControllerButton(rawButton) ?: rawButton
        when (event.action) {
            KeyEvent.ACTION_DOWN -> onButtonEvent?.invoke(button, true)
            KeyEvent.ACTION_UP -> onButtonEvent?.invoke(button, false)
        }
        return true
    }

    fun handleMotionEvent(event: MotionEvent): Boolean {
        if (!isGameControllerDevice(event.device ?: return false)) return false

        // Left stick
        val lx = event.getAxisValue(MotionEvent.AXIS_X)
        val ly = event.getAxisValue(MotionEvent.AXIS_Y)
        if (lx != 0f || ly != 0f) {
            onAnalogEvent?.invoke(lx, ly, 0)
        }

        // Right stick
        val rx = event.getAxisValue(MotionEvent.AXIS_Z)
        val ry = event.getAxisValue(MotionEvent.AXIS_RZ)
        if (rx != 0f || ry != 0f) {
            onAnalogEvent?.invoke(rx, ry, 1)
        }

        // D-pad via axes (some controllers report dpad as axis)
        val hatX = event.getAxisValue(MotionEvent.AXIS_HAT_X)
        val hatY = event.getAxisValue(MotionEvent.AXIS_HAT_Y)
        handleDpadAxis(hatX, hatY)

        return true
    }

    private fun handleDpadAxis(hatX: Float, hatY: Float) {
        onButtonEvent?.invoke(GamepadButton.DPAD_LEFT, hatX < -0.5f)
        onButtonEvent?.invoke(GamepadButton.DPAD_RIGHT, hatX > 0.5f)
        onButtonEvent?.invoke(GamepadButton.DPAD_UP, hatY < -0.5f)
        onButtonEvent?.invoke(GamepadButton.DPAD_DOWN, hatY > 0.5f)
    }

    fun setActiveController(controller: GameController) {
        _activeController.value = controller
    }

    private fun mapKeyToButton(keyCode: Int): GamepadButton? {
        return when (keyCode) {
            KeyEvent.KEYCODE_BUTTON_A, KeyEvent.KEYCODE_DPAD_CENTER -> GamepadButton.FACE_BOTTOM
            KeyEvent.KEYCODE_BUTTON_B -> GamepadButton.FACE_RIGHT
            KeyEvent.KEYCODE_BUTTON_X -> GamepadButton.FACE_LEFT
            KeyEvent.KEYCODE_BUTTON_Y -> GamepadButton.FACE_TOP
            KeyEvent.KEYCODE_BUTTON_L1 -> GamepadButton.L1
            KeyEvent.KEYCODE_BUTTON_R1 -> GamepadButton.R1
            KeyEvent.KEYCODE_BUTTON_L2 -> GamepadButton.L2
            KeyEvent.KEYCODE_BUTTON_R2 -> GamepadButton.R2
            KeyEvent.KEYCODE_BUTTON_THUMBL -> GamepadButton.L3
            KeyEvent.KEYCODE_BUTTON_THUMBR -> GamepadButton.R3
            KeyEvent.KEYCODE_BUTTON_START -> GamepadButton.START
            KeyEvent.KEYCODE_BUTTON_SELECT -> GamepadButton.SELECT
            KeyEvent.KEYCODE_DPAD_UP -> GamepadButton.DPAD_UP
            KeyEvent.KEYCODE_DPAD_DOWN -> GamepadButton.DPAD_DOWN
            KeyEvent.KEYCODE_DPAD_LEFT -> GamepadButton.DPAD_LEFT
            KeyEvent.KEYCODE_DPAD_RIGHT -> GamepadButton.DPAD_RIGHT
            else -> null
        }
    }

    private fun isGameController(device: InputDevice): Boolean {
        val sources = device.sources
        return (sources and InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD ||
                (sources and InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
    }

    private fun isGameControllerDevice(device: InputDevice): Boolean = isGameController(device)

    // InputManager.InputDeviceListener
    override fun onInputDeviceAdded(deviceId: Int) = refreshControllers()
    override fun onInputDeviceRemoved(deviceId: Int) {
        if (_activeController.value?.deviceId == deviceId) {
            _activeController.value = null
        }
        refreshControllers()
    }
    override fun onInputDeviceChanged(deviceId: Int) = refreshControllers()
}

data class GameController(
    val deviceId: Int,
    val name: String,
    val vendorId: Int,
    val productId: Int,
    val isExternal: Boolean
)
