## DragDropComponent — reusable drag-and-drop node; attach to any Character or InteractiveObject.
class_name DragDropComponent extends Node

signal drag_picked_up(global_position: Vector2)
signal drag_released(global_position: Vector2)

const DRAG_Z_INDEX: int = 10
const DRAG_SCALE: float = 1.1
const DEFAULT_DRAG_RADIUS: float = 64.0

@export var drag_target: Node2D

var _is_dragging: bool = false
var _drag_offset: Vector2 = Vector2.ZERO
var _original_z_index: int = 0
var _original_scale: Vector2 = Vector2.ONE


func _ready() -> void:
	if drag_target == null:
		drag_target = get_parent() as Node2D


func _input(event: InputEvent) -> void:
	if event is InputEventScreenTouch:
		_handle_press(event.pressed, event.position)
	elif event is InputEventScreenDrag and _is_dragging:
		_move_to(event.position)
	elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
		_handle_press(event.pressed, event.position)
	elif event is InputEventMouseMotion and _is_dragging:
		_move_to(event.position)


func _handle_press(pressed: bool, screen_pos: Vector2) -> void:
	if pressed and _is_position_over_target(screen_pos):
		_start_drag(screen_pos)
	elif not pressed and _is_dragging:
		_end_drag(screen_pos)


func _start_drag(screen_pos: Vector2) -> void:
	_is_dragging = true
	var world_pos: Vector2 = _screen_to_world(screen_pos)
	_drag_offset = drag_target.global_position - world_pos
	_original_z_index = drag_target.z_index
	_original_scale = drag_target.scale
	drag_target.z_index = DRAG_Z_INDEX
	drag_target.scale = _original_scale * DRAG_SCALE
	drag_picked_up.emit(drag_target.global_position)


func _end_drag(_screen_pos: Vector2) -> void:
	_is_dragging = false
	drag_target.z_index = _original_z_index
	drag_target.scale = _original_scale
	drag_released.emit(drag_target.global_position)


func _move_to(screen_pos: Vector2) -> void:
	var world_pos: Vector2 = _screen_to_world(screen_pos)
	drag_target.global_position = world_pos + _drag_offset


func _screen_to_world(screen_pos: Vector2) -> Vector2:
	var canvas_transform: Transform2D = drag_target.get_viewport().get_canvas_transform()
	return canvas_transform.affine_inverse() * screen_pos


func _is_position_over_target(screen_pos: Vector2) -> bool:
	if drag_target == null:
		return false
	var world_pos: Vector2 = _screen_to_world(screen_pos)
	var local_pos: Vector2 = drag_target.to_local(world_pos)
	var area: Area2D = drag_target.get_node_or_null("CollisionArea") as Area2D
	if area == null:
		return local_pos.length() < DEFAULT_DRAG_RADIUS
	for child in area.get_children():
		if child is CollisionShape2D and child.shape != null:
			return child.shape.get_rect().has_point(local_pos)
	return local_pos.length() < DEFAULT_DRAG_RADIUS
