extends CharacterBody3D

## How fast the player slides between tiles (units per second).
@export var move_speed: float = 6.0
## How fast the player rotates 90 degrees (degrees per second).
@export var turn_speed: float = 360.0

# Movement state
var _is_moving := false
var _is_turning := false

var _move_start := Vector3.ZERO
var _move_target := Vector3.ZERO
var _move_progress := 0.0

var _turn_start := 0.0
var _turn_target := 0.0
var _turn_progress := 0.0


func _ready() -> void:
	position = Grid.snap(position)
	# Snap rotation to nearest 90° so we stay grid-aligned
	rotation.y = roundf(rotation.y / (PI / 2.0)) * (PI / 2.0)


func _physics_process(delta: float) -> void:
	if _is_moving:
		_process_move(delta)
	elif _is_turning:
		_process_turn(delta)
	else:
		_handle_input()


func _handle_input() -> void:
	var forward := -transform.basis.z
	forward.y = 0.0
	forward = forward.normalized()

	# Forward / back
	if Input.is_action_just_pressed("move_forward"):
		_try_move(forward)
	elif Input.is_action_just_pressed("move_back"):
		_try_move(-forward)
	# Turn left / right
	elif Input.is_action_just_pressed("turn_left"):
		_start_turn(-1)
	elif Input.is_action_just_pressed("turn_right"):
		_start_turn(1)
	# Interact with entities on this tile
	elif Input.is_action_just_pressed("interact"):
		_try_interact()


func _try_move(direction: Vector3) -> void:
	direction = _snap_direction(direction)
	var target := position + direction * Grid.CELL_SIZE

	# Raycast to check for walls before moving
	var ray_start := position + Vector3(0, 0.5, 0)
	var ray_end := target + Vector3(0, 0.5, 0)

	var space := get_world_3d().direct_space_state
	var query := PhysicsRayQueryParameters3D.create(
		ray_start, ray_end, collision_mask
	)
	query.exclude = [get_rid()]
	var result := space.intersect_ray(query)

	print("--- MOVE ATTEMPT ---")
	print("  from: ", position, "  to: ", target)
	print("  direction: ", direction)
	print("  ray from: ", ray_start, "  ray to: ", ray_end)
	print("  collision_mask: ", collision_mask)
	if result.is_empty():
		print("  result: CLEAR - moving")
		_move_start = position
		_move_target = target
		_move_progress = 0.0
		_is_moving = true
	else:
		print("  result: BLOCKED")
		print("  hit collider: ", result.collider.name if result.collider else "unknown")
		print("  hit position: ", result.position)
		print("  hit normal: ", result.normal)


func _process_move(delta: float) -> void:
	var distance := _move_start.distance_to(_move_target)
	_move_progress += (move_speed * delta) / distance
	_move_progress = minf(_move_progress, 1.0)

	position = _move_start.lerp(_move_target, _move_progress)

	if _move_progress >= 1.0:
		position = _move_target
		_is_moving = false
		_check_step_on()


func _start_turn(direction: int) -> void:
	_turn_start = rotation.y
	_turn_target = _turn_start - direction * (PI / 2.0)
	_turn_progress = 0.0
	_is_turning = true


func _process_turn(delta: float) -> void:
	_turn_progress += (deg_to_rad(turn_speed) * delta) / abs(_turn_target - _turn_start)
	_turn_progress = minf(_turn_progress, 1.0)

	rotation.y = lerpf(_turn_start, _turn_target, _turn_progress)

	if _turn_progress >= 1.0:
		rotation.y = _turn_target
		_is_turning = false


func _try_interact() -> void:
	var my_cell := Grid.world_to_cell(position)

	# Same-cell interactables (statues, etc.)
	for node in get_tree().get_nodes_in_group("interactable"):
		var entity := node as Node3D
		if my_cell == Grid.world_to_cell(entity.global_position):
			entity.interact()
			return

	# Facing interactables (doors) — detected via their own Area3D
	for node in get_tree().get_nodes_in_group("facing_interactable"):
		if node.try_interact(self):
			return


func _check_step_on() -> void:
	var my_cell := Grid.world_to_cell(position)
	for node in get_tree().get_nodes_in_group("steppable"):
		var entity := node as Node3D
		if my_cell == Grid.world_to_cell(entity.global_position):
			entity.stepped_on()


func _snap_direction(dir: Vector3) -> Vector3:
	if absf(dir.x) >= absf(dir.z):
		return Vector3(signf(dir.x), 0.0, 0.0)
	else:
		return Vector3(0.0, 0.0, signf(dir.z))

