extends CharacterBody3D
class_name BaseUnit

## Base class for all units (players, enemies, NPCs) in the game
## Provides common functionality like health management and taking damage

signal health_changed(old_health: float, new_health: float)
signal died(killer_id: int)
signal respawned()

@export var max_health: float = 100.0
@export var can_respawn: bool = true
@export var respawn_delay: float = 3.0

var current_health: float = 100.0
var is_dead: bool = false
var _respawn_point: Vector3 = Vector3.ZERO

func _ready():
	current_health = max_health
	_respawn_point = global_position

func _enter_tree():
	set_multiplayer_authority(str(name).to_int())

## Take damage from an attacker
## Should only be called on the server for authority
@rpc("any_peer", "reliable")
func take_damage(amount: float, attacker_id: int = -1, knockback: float = 0.0, attacker_pos: Vector3 = Vector3.ZERO):
	# Only server can process damage
	if not multiplayer.is_server():
		return

	if is_dead:
		return

	# Apply blocking reduction if applicable (duck typing - check if method exists)
	var final_damage = amount
	var final_knockback = knockback
	var was_blocked = false
	if has_method("get_block_reduction"):
		var block_reduction = call("get_block_reduction")
		if block_reduction > 0.0:
			final_damage = amount * (1.0 - block_reduction)
			final_knockback = knockback * (1.0 - block_reduction)  # Reduce knockback too
			was_blocked = true
			print("Blocked! Damage reduced from ", amount, " to ", final_damage, " (", block_reduction * 100, "% reduction)")

	var old_health = current_health
	current_health = max(0, current_health - final_damage)

	# Broadcast health change to all clients
	rpc("sync_health", current_health)
	health_changed.emit(old_health, current_health)

	# Show damage number on all clients
	rpc("_show_damage_number", final_damage, was_blocked)

	# Play hurt animation on all clients
	rpc("_play_hurt_animation")

	# Apply knockback if applicable
	if final_knockback > 0.0 and attacker_pos != Vector3.ZERO:
		var knockback_dir = (global_position - attacker_pos).normalized()
		knockback_dir.y = 0.3  # Slight upward component
		rpc("_apply_knockback", knockback_dir * final_knockback)

	if current_health <= 0:
		_die(attacker_id)

## Heal the unit
@rpc("any_peer", "reliable")
func heal(amount: float):
	if not multiplayer.is_server():
		return

	if is_dead:
		return

	var old_health = current_health
	current_health = min(max_health, current_health + amount)

	rpc("sync_health", current_health)
	health_changed.emit(old_health, current_health)

## Sync health across all clients
@rpc("any_peer", "call_local", "reliable")
func sync_health(new_health: float):
	var old_health = current_health
	current_health = new_health
	health_changed.emit(old_health, current_health)

## Handle death
func _die(killer_id: int):
	if is_dead:
		return

	is_dead = true
	died.emit(killer_id)
	rpc("sync_death", killer_id)

	if can_respawn and multiplayer.is_server():
		await get_tree().create_timer(respawn_delay).timeout
		_respawn()

## Sync death state to all clients
@rpc("any_peer", "call_local", "reliable")
func sync_death(killer_id: int):
	is_dead = true
	died.emit(killer_id)
	# Subclasses should override to add visual effects, disable collision, etc.

## Respawn the unit
func _respawn():
	if not multiplayer.is_server():
		return

	is_dead = false
	current_health = max_health
	global_position = _respawn_point
	velocity = Vector3.ZERO

	rpc("sync_respawn", _respawn_point)
	respawned.emit()

## Sync respawn to all clients
@rpc("any_peer", "call_local", "reliable")
func sync_respawn(spawn_pos: Vector3):
	is_dead = false
	current_health = max_health
	global_position = spawn_pos
	velocity = Vector3.ZERO
	respawned.emit()

## Set the respawn point
func set_respawn_point(point: Vector3):
	_respawn_point = point

## Get health percentage (0.0 to 1.0)
func get_health_percent() -> float:
	return current_health / max_health if max_health > 0 else 0.0

## Show floating damage number
@rpc("any_peer", "call_local", "reliable")
func _show_damage_number(damage: float, was_blocked: bool):
	print("Spawning damage number: ", damage, " blocked: ", was_blocked, " on peer: ", multiplayer.get_unique_id())

	# Create damage number label
	var damage_label = Label3D.new()
	var damage_script = load("res://level/scripts/damage_number.gd")
	damage_label.set_script(damage_script)

	# Position above the character
	damage_label.position = global_position + Vector3(0, 2.5, 0)

	# Add to scene (not as child of this node, so it doesn't get affected by player movement)
	get_tree().get_current_scene().add_child(damage_label)

	# Set damage value and color
	damage_label.call("set_damage", damage, was_blocked)

## Apply knockback force
@rpc("any_peer", "call_local", "reliable")
func _apply_knockback(knockback_velocity: Vector3):
	# Only applies to CharacterBody3D (players)
	if self is CharacterBody3D:
		velocity += knockback_velocity
		print("Applied knockback: ", knockback_velocity)

## Play hurt animation (override in subclasses)
@rpc("any_peer", "call_local", "reliable")
func _play_hurt_animation():
	# Subclasses should implement this
	pass
