extends BaseUnit
class_name Character

const NORMAL_SPEED = 6.0
const SPRINT_SPEED = 10.0
const JUMP_VELOCITY = 10

enum SkinColor { BLUE, YELLOW, GREEN, RED }

@onready var nickname: Label3D = $PlayerNick/Nickname
@onready var health_label: Label3D = null  # Optional 3D health label

@export_category("Objects")
@export var _body: Node3D = null
@export var _spring_arm_offset: Node3D = null
@export var _weapon_attachment: BoneAttachment3D = null  # WeaponPoint bone attachment (main hand)
@export var _weapon_container: Node3D = null  # Container node for main hand weapons
@export var _offhand_attachment: BoneAttachment3D = null  # OffHandPoint bone attachment
@export var _offhand_container: Node3D = null  # Container node for off-hand weapons

@export_category("Skin Colors")
@export var blue_texture : CompressedTexture2D
@export var yellow_texture : CompressedTexture2D
@export var green_texture : CompressedTexture2D
@export var red_texture : CompressedTexture2D

@onready var _bottom_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Bottom")
@onready var _chest_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Chest")
@onready var _face_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Face")
@onready var _limbs_head_mesh: MeshInstance3D = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/Llimbs and head")

var _current_speed: float
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

# Weapon system
var equipped_weapon: BaseWeapon = null  # Main hand weapon
var equipped_offhand: BaseWeapon = null  # Off-hand weapon
var _nearby_weapons: Array[WorldWeapon] = []
var is_blocking: bool = false

# Attack system
@export var attack_damage: float = 10.0
@export var attack_range: float = 3.0
@export var attack_cooldown: float = 0.5
var _attack_timer: float = 0.0

# Dash system
@export var dash_speed_multiplier: float = 2.0
@export var dash_duration: float = 0.25
@export var dash_cooldown: float = 4.0
var _dash_timer: float = 0.0
var _dash_cooldown_timer: float = 0.0
var _is_dashing: bool = false
var _dash_direction: Vector3 = Vector3.ZERO

# UI Signals
signal dash_cooldown_updated(remaining: float, total: float)
signal attack_cooldown_updated(remaining: float, total: float)
signal weapon_equipped_changed()

func _enter_tree():
	super._enter_tree()
	$SpringArmOffset/SpringArm3D/Camera3D.current = is_multiplayer_authority()

func _ready():
	super._ready()
	set_respawn_point(Vector3(0, 5, 0))

	# Capture mouse for local player
	if is_multiplayer_authority():
		Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

	# Auto-find body node (needed for instanced scenes where @export NodePath doesn't work reliably)
	if _body == null:
		for child in get_children():
			if child.name == "Armature" or child.name == "3DGodotRobot":
				_body = child
				print("Auto-found _body: ", child.name)
				break
		if _body == null:
			push_error("Could not find body node (Armature or 3DGodotRobot)!")

	# Auto-find spring arm offset
	if _spring_arm_offset == null:
		if has_node("SpringArmOffset"):
			_spring_arm_offset = get_node("SpringArmOffset")
			print("Auto-found _spring_arm_offset")
		else:
			push_error("Could not find SpringArmOffset!")

	# Auto-find weapon attachments if not set
	if _weapon_attachment == null:
		if has_node("Armature/Skeleton3D/WeaponPoint"):
			_weapon_attachment = get_node("Armature/Skeleton3D/WeaponPoint")
			print("Auto-found _weapon_attachment")
		elif has_node("3DGodotRobot/RobotArmature/Skeleton3D/BoneAttachment3D"):
			_weapon_attachment = get_node("3DGodotRobot/RobotArmature/Skeleton3D/BoneAttachment3D")
			print("Auto-found _weapon_attachment (robot)")
		else:
			print("WARNING: WeaponPoint not found! Check if you've added BoneAttachment3D nodes.")
			if has_node("Armature/Skeleton3D"):
				print("Skeleton3D children:")
				var skeleton = get_node("Armature/Skeleton3D")
				for child in skeleton.get_children():
					print("  - ", child.name, " (", child.get_class(), ")")

	# Auto-find weapon container
	if _weapon_container == null and _weapon_attachment:
		var container = _weapon_attachment.get_node_or_null("WeaponContainer")
		if container:
			_weapon_container = container
			print("Auto-found _weapon_container")

	# Auto-find offhand attachment
	if _offhand_attachment == null:
		if has_node("Armature/Skeleton3D/OffhandPoint"):
			_offhand_attachment = get_node("Armature/Skeleton3D/OffhandPoint")
			print("Auto-found _offhand_attachment")
		elif has_node("3DGodotRobot/RobotArmature/Skeleton3D/OffHandPoint"):
			_offhand_attachment = get_node("3DGodotRobot/RobotArmature/Skeleton3D/OffHandPoint")
			print("Auto-found _offhand_attachment (robot)")
		else:
			print("WARNING: OffhandPoint not found!")

	# Auto-find offhand container
	if _offhand_container == null and _offhand_attachment:
		var container = _offhand_attachment.get_node_or_null("OffhandContainer")
		if container:
			_offhand_container = container
			print("Auto-found _offhand_container")

	# Try to get optional health label
	if has_node("PlayerNick/HealthLabel"):
		health_label = get_node("PlayerNick/HealthLabel")

	# Connect health signals
	health_changed.connect(_on_health_changed)
	died.connect(_on_died)
	respawned.connect(_on_respawned)

	# Update health display
	_update_health_display()

	# Setup weapon pickup detection area
	_setup_weapon_pickup_area()

	# Auto-find weapon attachment if not set
	if _weapon_attachment == null:
		var bone_attach = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/BoneAttachment3D")
		if bone_attach:
			_weapon_attachment = bone_attach
			print("Auto-found weapon attachment point")
		else:
			push_warning("Could not find BoneAttachment3D for weapons!")

	# Auto-find weapon container if not set
	if _weapon_container == null and _weapon_attachment:
		var container = _weapon_attachment.get_node_or_null("WeaponContainer")
		if container:
			_weapon_container = container
			print("Auto-found main hand weapon container")
		else:
			push_warning("Could not find WeaponContainer! Weapons will attach directly to BoneAttachment3D.")

	# Auto-find off-hand attachment if not set
	if _offhand_attachment == null:
		var offhand_attach = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/OffHandPoint")
		if offhand_attach:
			_offhand_attachment = offhand_attach
			print("Auto-found off-hand attachment point")

	# Auto-find off-hand container if not set
	if _offhand_container == null and _offhand_attachment:
		var container = _offhand_attachment.get_node_or_null("OffHandContainer")
		if container:
			_offhand_container = container
			print("Auto-found off-hand weapon container")

func _physics_process(delta):
	# Check if multiplayer is ready
	if multiplayer.multiplayer_peer == null:
		return

	if not is_multiplayer_authority():
		return

	var current_scene = get_tree().get_current_scene()
	if current_scene and current_scene.has_method("is_chat_visible") and current_scene.is_chat_visible() and is_on_floor():
		freeze()
		return

	if not is_on_floor():
		velocity.y -= gravity * delta
		_body.animate(velocity)

	if is_on_floor():
		if Input.is_action_just_pressed("jump"):
			velocity.y = JUMP_VELOCITY
	else:
		velocity.y -= gravity * delta

	_move()
	move_and_slide()
	_body.animate(velocity)

func _process(delta):
	# Check if multiplayer is ready
	if multiplayer.multiplayer_peer == null:
		return

	if not is_multiplayer_authority():
		return

	_check_fall_and_respawn()

	# Update attack cooldown
	if _attack_timer > 0:
		_attack_timer -= delta
		attack_cooldown_updated.emit(_attack_timer, attack_cooldown)

	# Update dash timers
	if _dash_timer > 0:
		_dash_timer -= delta
		if _dash_timer <= 0:
			_is_dashing = false

	if _dash_cooldown_timer > 0:
		_dash_cooldown_timer -= delta
		dash_cooldown_updated.emit(_dash_cooldown_timer, dash_cooldown)

	# Handle dash input
	if Input.is_action_just_pressed("dash") and _dash_cooldown_timer <= 0 and not is_dead and is_on_floor():
		_perform_dash()

	# Handle block input
	if Input.is_action_pressed("block") and not is_dead:
		_try_block()
	else:
		is_blocking = false

	# Handle attack input
	if Input.is_action_just_pressed("attack") and not is_dead and not _is_dashing and not is_blocking:
		_perform_attack()

	# Handle weapon pickup/drop
	if Input.is_action_just_pressed("pickup") and not is_dead:
		print("Pickup pressed! Main: ", equipped_weapon != null, " OffHand: ", equipped_offhand != null, " Nearby: ", _nearby_weapons.size())

		# Try to pickup nearby weapon
		if _nearby_weapons.size() > 0:
			_pickup_nearest_weapon()
		# If no nearby weapons, drop what we're holding (main hand first, then off-hand)
		elif equipped_weapon:
			# Tell server to drop (server will handle spawning)
			if multiplayer.is_server():
				drop_weapon(false)  # main hand
			else:
				rpc_id(1, "drop_weapon", false)  # Only send to server
		elif equipped_offhand:
			# Drop off-hand
			if multiplayer.is_server():
				drop_weapon(true)  # off-hand
			else:
				rpc_id(1, "drop_weapon", true)
		else:
			print("No weapons nearby to pick up")

func freeze():
	velocity.x = 0
	velocity.z = 0
	_current_speed = 0
	_body.animate(Vector3.ZERO)

func _move() -> void:
	# If dashing, use dash movement
	if _is_dashing:
		velocity.x = _dash_direction.x * _current_speed * dash_speed_multiplier
		velocity.z = _dash_direction.z * _current_speed * dash_speed_multiplier
		_body.apply_rotation(velocity)
		return

	var _input_direction: Vector2 = Vector2.ZERO
	if is_multiplayer_authority():
		_input_direction = Input.get_vector(
			"move_left", "move_right",
			"move_forward", "move_backward"
			)

	var _direction: Vector3 = transform.basis * Vector3(_input_direction.x, 0, _input_direction.y).normalized()

	is_running()
	_direction = _direction.rotated(Vector3.UP, _spring_arm_offset.rotation.y)

	if _direction:
		velocity.x = _direction.x * _current_speed
		velocity.z = _direction.z * _current_speed
		_body.apply_rotation(velocity)
		return

	velocity.x = move_toward(velocity.x, 0, _current_speed)
	velocity.z = move_toward(velocity.z, 0, _current_speed)

func is_running() -> bool:
	if Input.is_action_pressed("shift"):
		_current_speed = SPRINT_SPEED
		return true
	else:
		_current_speed = NORMAL_SPEED
		return false

func _check_fall_and_respawn():
	if global_transform.origin.y < -15.0 and multiplayer.is_server():
		# Use BaseUnit's respawn system
		_respawn()

@rpc("any_peer", "reliable")
func change_nick(new_nick: String):
	if nickname:
		nickname.text = new_nick

func get_texture_from_name(skin_color: SkinColor) -> CompressedTexture2D:
	match skin_color:
		SkinColor.BLUE: return blue_texture
		SkinColor.GREEN: return green_texture
		SkinColor.RED: return red_texture
		SkinColor.YELLOW: return yellow_texture
		_: return blue_texture

@rpc("any_peer", "reliable")
func set_player_skin(skin_name: SkinColor) -> void:
	var texture = get_texture_from_name(skin_name)

	set_mesh_texture(_bottom_mesh, texture)
	set_mesh_texture(_chest_mesh, texture)
	set_mesh_texture(_face_mesh, texture)
	set_mesh_texture(_limbs_head_mesh, texture)

func set_mesh_texture(mesh_instance: MeshInstance3D, texture: CompressedTexture2D) -> void:
	if mesh_instance:
		var material := mesh_instance.get_surface_override_material(0)
		if material and material is StandardMaterial3D:
			var new_material := material
			new_material.albedo_texture = texture
			mesh_instance.set_surface_override_material(0, new_material)

## Block system
func _try_block():
	# Check if we have a weapon that can block
	var can_block_weapon = false

	if equipped_weapon and equipped_weapon.can_block():
		can_block_weapon = true
	elif equipped_offhand and equipped_offhand.can_block():
		can_block_weapon = true

	is_blocking = can_block_weapon

	# Could add blocking animation here
	# if is_blocking and _body:
	#     _body.play_block_animation()

## Get block reduction amount (0.0 to 1.0)
func get_block_reduction() -> float:
	if not is_blocking:
		return 0.0

	var reduction = 0.0

	# Use the highest block reduction from equipped weapons
	if equipped_weapon and equipped_weapon.can_block():
		reduction = max(reduction, equipped_weapon.get_block_reduction())

	if equipped_offhand and equipped_offhand.can_block():
		reduction = max(reduction, equipped_offhand.get_block_reduction())

	return reduction

## Attack system
func _perform_attack():
	if not is_multiplayer_authority() or is_dead:
		return

	# Use main hand weapon if available
	if equipped_weapon and equipped_weapon.can_attack():
		equipped_weapon.perform_attack()
		return

	# Or use off-hand weapon if available
	if equipped_offhand and equipped_offhand.can_attack():
		equipped_offhand.perform_attack()
		return

	# Fallback to default unarmed attack
	# Don't attack if already attacking
	if _body and _body.animation_player and _body.animation_player.current_animation == "Attack1":
		return

	if _attack_timer > 0:
		return

	_attack_timer = attack_cooldown

	# Play attack animation once
	if _body:
		_body.play_attack()

	# Find nearest enemy in range
	var space_state = get_world_3d().direct_space_state
	var query = PhysicsShapeQueryParameters3D.new()
	var sphere = SphereShape3D.new()
	sphere.radius = attack_range
	query.shape = sphere
	query.transform = global_transform
	query.collision_mask = 1  # Player layer

	var results = space_state.intersect_shape(query)

	for result in results:
		var hit_body = result["collider"]
		if hit_body != self and hit_body is BaseUnit:
			var attacker_id = multiplayer.get_unique_id()

			# If we're the server, apply damage directly (default unarmed knockback)
			if multiplayer.is_server():
				_server_apply_damage(hit_body.name, attack_damage, attacker_id, 5.0, global_position)
			else:
				# Otherwise, request server to apply damage
				rpc_id(1, "_server_apply_damage", hit_body.name, attack_damage, attacker_id, 5.0, global_position)
			break  # Only hit one target per attack

## Server-side damage application
@rpc("any_peer", "reliable")
func _server_apply_damage(target_name: String, damage: float, attacker_id: int, knockback: float = 0.0, attacker_pos: Vector3 = Vector3.ZERO):
	if not multiplayer.is_server():
		return

	# Get the target from the players container
	var level = get_tree().get_current_scene()
	if not level or not level.has_node("PlayersContainer"):
		return

	var players_container = level.get_node("PlayersContainer")
	if not players_container.has_node(target_name):
		return

	var target = players_container.get_node(target_name)
	if target and target is BaseUnit:
		target.take_damage(damage, attacker_id, knockback, attacker_pos)

## Health display and callbacks
# Old 2D health UI - now handled by HUD Manager
# Kept for reference, but no longer used

func _update_health_display():
	# Update 3D label if it exists (for other players to see)
	if health_label:
		health_label.text = "HP: %d/%d" % [int(current_health), int(max_health)]

	# 2D UI is now handled by HUD Manager autoload

func _on_health_changed(_old_health: float, _new_health: float):
	_update_health_display()

func _on_died(killer_id: int):
	# Disable player when dead
	set_physics_process(false)
	set_process(false)

	# Visual feedback - could add death animation here
	if _body:
		_body.visible = false

	# Print death message
	var killer_name = "Unknown"
	if Network.players.has(killer_id):
		killer_name = Network.players[killer_id]["nick"]

	if is_multiplayer_authority():
		print("You were killed by ", killer_name)

		# Show death message on UI
		if has_node("HealthUI/HealthText"):
			get_node("HealthUI/HealthText").text = "DEAD - Respawning..."

func _on_respawned():
	# Re-enable player
	set_physics_process(true)
	set_process(true)

	if _body:
		_body.visible = true

	_update_health_display()

	if is_multiplayer_authority():
		print("You respawned!")

## Dash system
func _perform_dash():
	if not is_multiplayer_authority() or is_dead or not is_on_floor():
		return

	# Get current movement direction or use forward if no input
	var _input_direction: Vector2 = Input.get_vector(
		"move_left", "move_right",
		"move_forward", "move_backward"
	)

	var _direction: Vector3
	if _input_direction.length() > 0:
		# Dash in input direction
		_direction = transform.basis * Vector3(_input_direction.x, 0, _input_direction.y).normalized()
		_direction = _direction.rotated(Vector3.UP, _spring_arm_offset.rotation.y)
	else:
		# Dash forward if no input
		_direction = -transform.basis.z
		_direction = _direction.rotated(Vector3.UP, _spring_arm_offset.rotation.y)

	# Set dash parameters
	_dash_direction = _direction
	_is_dashing = true
	_dash_timer = dash_duration
	_dash_cooldown_timer = dash_cooldown

	# Animation is handled by the Body's animate function (Jump animation plays during dash)

## Override hurt animation from BaseUnit
func _play_hurt_animation():
	if _body and _body.animation_player:
		# Try to play a hurt/hit animation if it exists
		if _body.animation_player.has_animation("Hurt"):
			_body.animation_player.play("Hurt")
		elif _body.animation_player.has_animation("Hit"):
			_body.animation_player.play("Hit")
		elif _body.animation_player.has_animation("TakeDamage"):
			_body.animation_player.play("TakeDamage")
		else:
			# Fallback: briefly flash the character red
			_flash_hurt()

## Flash effect when no hurt animation exists
func _flash_hurt():
	if not _body:
		return

	# Only works if _body has a modulate property (CanvasItem or some Node3D with visual children)
	if "modulate" in _body:
		# Store original modulate
		var original_modulate = _body.modulate

		# Flash red
		_body.modulate = Color(1.5, 0.5, 0.5, 1.0)

		# Return to normal after a brief moment
		await get_tree().create_timer(0.15).timeout
		if _body:
			_body.modulate = original_modulate
	# If no modulate, just skip the visual effect

## Weapon System
func _setup_weapon_pickup_area():
	# Create an Area3D to detect nearby weapons
	var pickup_area = Area3D.new()
	pickup_area.name = "WeaponPickupArea"
	pickup_area.collision_layer = 0
	pickup_area.collision_mask = 4  # Will detect WorldWeapons (we'll use layer 3)
	add_child(pickup_area)

	# Create collision shape for pickup range
	var pickup_collision = CollisionShape3D.new()
	var sphere = SphereShape3D.new()
	sphere.radius = 2.0  # Pickup range
	pickup_collision.shape = sphere
	pickup_area.add_child(pickup_collision)

	# Connect signals to track nearby weapons
	pickup_area.area_entered.connect(_on_weapon_area_entered)
	pickup_area.area_exited.connect(_on_weapon_area_exited)

func _on_weapon_area_entered(area: Area3D):
	# Check if the area belongs to a WorldWeapon
	var weapon = area.get_parent()
	if weapon is WorldWeapon and weapon not in _nearby_weapons:
		_nearby_weapons.append(weapon)
		if is_multiplayer_authority():
			print("Weapon nearby: ", weapon.weapon_data.weapon_name if weapon.weapon_data else "Unknown")

func _on_weapon_area_exited(area: Area3D):
	# Remove weapon from nearby list
	var weapon = area.get_parent()
	if weapon is WorldWeapon and weapon in _nearby_weapons:
		_nearby_weapons.erase(weapon)
		if is_multiplayer_authority():
			print("Weapon left range: ", weapon.weapon_data.weapon_name if weapon.weapon_data else "Unknown")

## Equip a weapon from WorldWeapon data (receives resource path)
@rpc("any_peer", "call_local", "reliable")
func equip_weapon_from_world(weapon_data_path: String):
	print("[Client ", multiplayer.get_unique_id(), "] equip_weapon_from_world called for: ", weapon_data_path)
	var data = load(weapon_data_path) as WeaponData
	if data:
		equip_weapon(data)
		print("[Client ", multiplayer.get_unique_id(), "] Equipped weapon: ", data.weapon_name)
	else:
		push_error("Failed to load weapon data from: " + weapon_data_path)

## Equip a weapon with given data
func equip_weapon(data: WeaponData):
	# Determine which hand based on weapon type
	var is_offhand = (data.hand_type == WeaponData.Hand.OFF_HAND)

	# Unequip current weapon in that hand first
	if is_offhand:
		if equipped_offhand:
			unequip_weapon(true)
	else:
		if equipped_weapon:
			unequip_weapon(false)

	# Determine where to attach the weapon
	var attach_point: Node3D
	if is_offhand:
		attach_point = _offhand_container if _offhand_container else _offhand_attachment
	else:
		attach_point = _weapon_container if _weapon_container else _weapon_attachment

	if not attach_point:
		push_error("No weapon attachment point found for ", "off-hand" if is_offhand else "main hand")
		return

	# Create new weapon instance
	var weapon = BaseWeapon.new()
	weapon.weapon_data = data
	weapon.name = "EquippedOffHand" if is_offhand else "EquippedWeapon"
	weapon.set_owner_character(self)

	# Attach to weapon container (or bone attachment if no container)
	attach_point.add_child(weapon)

	# Store reference
	if is_offhand:
		equipped_offhand = weapon
	else:
		equipped_weapon = weapon

	if is_multiplayer_authority():
		print("Equipped: ", data.weapon_name, " to ", attach_point.name)
		weapon_equipped_changed.emit()

## Unequip current weapon (local only)
func unequip_weapon(is_offhand: bool = false):
	if is_offhand:
		if equipped_offhand:
			equipped_offhand.queue_free()
			equipped_offhand = null
	else:
		if equipped_weapon:
			equipped_weapon.queue_free()
			equipped_weapon = null

	if is_multiplayer_authority():
		weapon_equipped_changed.emit()

## Sync unequip across all clients
@rpc("any_peer", "call_local", "reliable")
func _unequip_weapon_sync(is_offhand: bool = false):
	unequip_weapon(is_offhand)

## Drop currently equipped weapon
@rpc("any_peer", "reliable")
func drop_weapon(is_offhand: bool = false):
	var has_weapon = equipped_offhand if is_offhand else equipped_weapon
	print("drop_weapon called on peer ", multiplayer.get_unique_id(), " is_server: ", multiplayer.is_server(), " has weapon: ", has_weapon != null, " offhand: ", is_offhand)

	if not has_weapon:
		print("No weapon equipped in ", "off-hand" if is_offhand else "main hand", ", cannot drop")
		return

	# Only server spawns the world weapon
	if multiplayer.is_server():
		print("Server spawning dropped weapon")
		_spawn_world_weapon(has_weapon.weapon_data)
		# Tell all clients to unequip
		rpc("_unequip_weapon_sync", is_offhand)

	# Unequip locally
	unequip_weapon(is_offhand)

	if is_multiplayer_authority():
		print("Dropped weapon from ", "off-hand" if is_offhand else "main hand")

## Spawn a weapon in the world (server only)
func _spawn_world_weapon(data: WeaponData):
	if not multiplayer.is_server():
		return

	# Get the resource path
	var resource_path = data.resource_path
	if resource_path == "":
		push_error("WeaponData has no resource path! Make sure to save it as a .tres file")
		return

	# Position in front of player
	var spawn_pos = global_position + (-transform.basis.z * 2.0)
	spawn_pos.y += 1.0  # Spawn at chest height

	# Calculate forward velocity
	var velocity = -transform.basis.z * 3.0

	# Tell level to spawn weapon on all clients
	var level = get_tree().get_current_scene()
	if level and level.has_method("spawn_world_weapon"):
		# Increment the level's weapon counter
		level._weapon_spawn_counter += 1
		level.rpc("spawn_world_weapon", resource_path, spawn_pos, velocity, level._weapon_spawn_counter)

## Pick up nearest weapon
func _pickup_nearest_weapon():
	if _nearby_weapons.size() == 0:
		return

	# Find closest weapon
	var closest_weapon: WorldWeapon = null
	var closest_distance: float = INF

	for weapon in _nearby_weapons:
		if not is_instance_valid(weapon):
			continue

		var distance = global_position.distance_to(weapon.global_position)
		if distance < closest_distance:
			closest_distance = distance
			closest_weapon = weapon

	if closest_weapon:
		# Check what hand this weapon goes in
		var weapon_hand_type = closest_weapon.weapon_data.hand_type if closest_weapon.weapon_data else WeaponData.Hand.MAIN_HAND
		var is_offhand = (weapon_hand_type == WeaponData.Hand.OFF_HAND)
		var slot_occupied = equipped_offhand if is_offhand else equipped_weapon

		# If the slot is occupied, drop it first
		if slot_occupied:
			print("Slot occupied, dropping current weapon before picking up")
			if multiplayer.is_server():
				drop_weapon(is_offhand)
			else:
				rpc_id(1, "drop_weapon", is_offhand)
			# Wait a tiny bit for the drop to complete
			await get_tree().create_timer(0.1).timeout

		# Request server to pickup (server validates)
		if multiplayer.is_server():
			closest_weapon.try_pickup(multiplayer.get_unique_id())
		else:
			closest_weapon.rpc_id(1, "try_pickup", multiplayer.get_unique_id())
