package com.lazy.emulate.ui.components

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.lazy.emulate.input.ButtonPosition
import com.lazy.emulate.input.ControllerLayout
import com.lazy.emulate.input.GamepadButton
import kotlin.math.atan2
import kotlin.math.sqrt

@Composable
fun TouchControllerOverlay(
    layout: ControllerLayout,
    onButtonPressed: (GamepadButton) -> Unit,
    onButtonReleased: (GamepadButton) -> Unit,
    onAnalogStick: (Float, Float, Int) -> Unit,
    modifier: Modifier = Modifier
) {
    BoxWithConstraints(modifier = modifier.fillMaxSize()) {
        val screenWidth = constraints.maxWidth.toFloat()
        val screenHeight = constraints.maxHeight.toFloat()

        // D-Pad
        DPad(
            centerX = layout.dpadPosition.x * screenWidth,
            centerY = layout.dpadPosition.y * screenHeight,
            size = layout.dpadSize * screenWidth,
            opacity = layout.opacity,
            onDirectionPressed = { onButtonPressed(it) },
            onDirectionReleased = { onButtonReleased(it) }
        )

        // Analog sticks
        layout.leftStickPosition?.let { pos ->
            AnalogStick(
                centerX = pos.x * screenWidth,
                centerY = pos.y * screenHeight,
                size = layout.stickSize * screenWidth,
                opacity = layout.opacity,
                onStickMoved = { x, y -> onAnalogStick(x, y, 0) }
            )
        }
        layout.rightStickPosition?.let { pos ->
            AnalogStick(
                centerX = pos.x * screenWidth,
                centerY = pos.y * screenHeight,
                size = layout.stickSize * screenWidth,
                opacity = layout.opacity,
                onStickMoved = { x, y -> onAnalogStick(x, y, 1) }
            )
        }

        // Face buttons and shoulders
        layout.buttons.forEach { buttonPos ->
            TouchButton(
                buttonPosition = buttonPos,
                screenWidth = screenWidth,
                screenHeight = screenHeight,
                opacity = layout.opacity,
                onPressed = { onButtonPressed(buttonPos.button) },
                onReleased = { onButtonReleased(buttonPos.button) }
            )
        }
    }
}

@Composable
private fun TouchButton(
    buttonPosition: ButtonPosition,
    screenWidth: Float,
    screenHeight: Float,
    opacity: Float,
    onPressed: () -> Unit,
    onReleased: () -> Unit
) {
    val sizePx = buttonPosition.size * screenWidth
    val density = LocalDensity.current

    val sizeDp = with(density) { sizePx.toDp() }
    val xDp = with(density) { (buttonPosition.x * screenWidth - sizePx / 2).toDp() }
    val yDp = with(density) { (buttonPosition.y * screenHeight - sizePx / 2).toDp() }

    var isPressed by remember { mutableStateOf(false) }
    val color = if (isPressed) Color.White.copy(alpha = opacity * 0.8f) else Color.White.copy(alpha = opacity * 0.4f)

    Box(
        modifier = Modifier
            .offset(x = xDp, y = yDp)
            .size(sizeDp)
            .pointerInput(Unit) {
                awaitPointerEventScope {
                    while (true) {
                        val event = awaitPointerEvent()
                        val wasPressed = isPressed
                        isPressed = event.changes.any { it.pressed }
                        if (isPressed && !wasPressed) onPressed()
                        if (!isPressed && wasPressed) onReleased()
                    }
                }
            }
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            drawCircle(
                color = color,
                radius = size.minDimension / 2,
                style = Stroke(width = 3.dp.toPx())
            )
            if (isPressed) {
                drawCircle(
                    color = color.copy(alpha = color.alpha * 0.3f),
                    radius = size.minDimension / 2
                )
            }
        }
    }
}

@Composable
private fun DPad(
    centerX: Float,
    centerY: Float,
    size: Float,
    opacity: Float,
    onDirectionPressed: (GamepadButton) -> Unit,
    onDirectionReleased: (GamepadButton) -> Unit
) {
    val density = LocalDensity.current
    val sizeDp = with(density) { size.toDp() }
    val xDp = with(density) { (centerX - size / 2).toDp() }
    val yDp = with(density) { (centerY - size / 2).toDp() }

    // Track which directions are currently held
    var activeDirections by remember { mutableStateOf(emptySet<GamepadButton>()) }
    val color = Color.White.copy(alpha = opacity * 0.4f)

    Box(
        modifier = Modifier
            .offset(x = xDp, y = yDp)
            .size(sizeDp)
            .pointerInput(Unit) {
                awaitPointerEventScope {
                    while (true) {
                        val event = awaitPointerEvent()
                        val change = event.changes.firstOrNull() ?: continue
                        change.consume()

                        val newDirections = mutableSetOf<GamepadButton>()

                        if (change.pressed) {
                            val dx = change.position.x - this@pointerInput.size.width / 2f
                            val dy = change.position.y - this@pointerInput.size.height / 2f
                            val dist = sqrt(dx * dx + dy * dy)
                            val deadZone = this@pointerInput.size.width * 0.12f

                            if (dist > deadZone) {
                                // Use thresholds instead of exclusive angles to allow diagonals
                                val nx = dx / dist
                                val ny = dy / dist
                                val threshold = 0.38f // ~22 degrees — allows diagonal input

                                if (nx > threshold) newDirections.add(GamepadButton.DPAD_RIGHT)
                                if (nx < -threshold) newDirections.add(GamepadButton.DPAD_LEFT)
                                if (ny > threshold) newDirections.add(GamepadButton.DPAD_DOWN)
                                if (ny < -threshold) newDirections.add(GamepadButton.DPAD_UP)
                            }
                        }

                        // Release directions no longer held
                        for (dir in activeDirections - newDirections) {
                            onDirectionReleased(dir)
                        }
                        // Press newly held directions
                        for (dir in newDirections - activeDirections) {
                            onDirectionPressed(dir)
                        }
                        activeDirections = newDirections
                    }
                }
            }
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            val armWidth = size * 0.3f
            val center = Offset(size / 2, size / 2)

            // Horizontal arm
            drawRoundRect(
                color = if (GamepadButton.DPAD_LEFT in activeDirections || GamepadButton.DPAD_RIGHT in activeDirections)
                    color.copy(alpha = color.alpha * 1.5f) else color,
                topLeft = Offset(0f, center.y - armWidth / 2),
                size = androidx.compose.ui.geometry.Size(size, armWidth),
                cornerRadius = androidx.compose.ui.geometry.CornerRadius(8f, 8f),
                style = Stroke(width = 2.dp.toPx())
            )
            // Vertical arm
            drawRoundRect(
                color = if (GamepadButton.DPAD_UP in activeDirections || GamepadButton.DPAD_DOWN in activeDirections)
                    color.copy(alpha = color.alpha * 1.5f) else color,
                topLeft = Offset(center.x - armWidth / 2, 0f),
                size = androidx.compose.ui.geometry.Size(armWidth, size),
                cornerRadius = androidx.compose.ui.geometry.CornerRadius(8f, 8f),
                style = Stroke(width = 2.dp.toPx())
            )
        }
    }
}

@Composable
private fun AnalogStick(
    centerX: Float,
    centerY: Float,
    size: Float,
    opacity: Float,
    onStickMoved: (Float, Float) -> Unit
) {
    val density = LocalDensity.current
    val sizeDp = with(density) { size.toDp() }
    val xDp = with(density) { (centerX - size / 2).toDp() }
    val yDp = with(density) { (centerY - size / 2).toDp() }

    var stickOffset by remember { mutableStateOf(Offset.Zero) }
    val maxRadius = size / 2 * 0.7f
    val color = Color.White.copy(alpha = opacity * 0.4f)

    Box(
        modifier = Modifier
            .offset(x = xDp, y = yDp)
            .size(sizeDp)
            .pointerInput(Unit) {
                detectDragGestures(
                    onDragEnd = {
                        stickOffset = Offset.Zero
                        onStickMoved(0f, 0f)
                    },
                    onDragCancel = {
                        stickOffset = Offset.Zero
                        onStickMoved(0f, 0f)
                    }
                ) { _, dragAmount ->
                    val newOffset = stickOffset + dragAmount
                    val dist = sqrt(newOffset.x * newOffset.x + newOffset.y * newOffset.y)
                    stickOffset = if (dist > maxRadius) {
                        newOffset * (maxRadius / dist)
                    } else {
                        newOffset
                    }
                    onStickMoved(stickOffset.x / maxRadius, stickOffset.y / maxRadius)
                }
            }
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            val center = Offset(size / 2, size / 2)
            // Outer ring
            drawCircle(
                color = color,
                radius = size / 2 - 4,
                center = center,
                style = Stroke(width = 2.dp.toPx())
            )
            // Inner stick
            drawCircle(
                color = color.copy(alpha = color.alpha * 1.5f),
                radius = size * 0.15f,
                center = center + stickOffset
            )
        }
    }
}
