extends Node3D
class_name BaseWeapon

## Base class for equipped weapons
## Attached to player's hand via BoneAttachment3D
## Provides common weapon functionality and stats

signal attack_performed()

@export var weapon_data: WeaponData

# Runtime references
var owner_character: Character = null
var _mesh_instance: Node3D = null
var _attack_timer: float = 0.0

func _ready():
	if weapon_data and weapon_data.mesh_scene:
		_spawn_mesh()

func _process(delta):
	if _attack_timer > 0:
		_attack_timer -= delta

## Spawn the visual mesh for this weapon
func _spawn_mesh():
	# Remove old mesh if exists
	if _mesh_instance:
		_mesh_instance.queue_free()

	# Instantiate new mesh
	_mesh_instance = weapon_data.mesh_scene.instantiate()
	add_child(_mesh_instance)

## Perform an attack with this weapon
## Called by the character who owns this weapon
func perform_attack() -> bool:
	if not weapon_data or not owner_character:
		return false

	# Check cooldown
	if _attack_timer > 0:
		return false

	_attack_timer = weapon_data.attack_cooldown

	# Notify owner character of attack cooldown (for UI)
	if owner_character and owner_character.is_multiplayer_authority():
		owner_character._attack_timer = weapon_data.attack_cooldown

	# Play attack animation on owner
	if owner_character._body:
		owner_character._body.play_attack()

	# Delay damage until animation hits (roughly 70% through the animation)
	# This makes the damage apply when the swing actually connects
	var damage_delay = weapon_data.attack_cooldown * 0.4  # Adjust this multiplier to change when damage happens
	get_tree().create_timer(damage_delay).timeout.connect(_find_and_damage_targets)

	attack_performed.emit()
	return true

## Find targets in range and apply damage
func _find_and_damage_targets():
	if not owner_character:
		return

	# Check if the owner character has authority (not this node)
	if not owner_character.is_multiplayer_authority():
		return

	var space_state = get_world_3d().direct_space_state
	var query = PhysicsShapeQueryParameters3D.new()
	var sphere = SphereShape3D.new()
	sphere.radius = weapon_data.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 != owner_character and hit_body is BaseUnit:
			var attacker_id = multiplayer.get_unique_id()

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

## Check if weapon can attack
func can_attack() -> bool:
	return _attack_timer <= 0

## Set the character who owns this weapon
func set_owner_character(character: Character):
	owner_character = character

## Get weapon stats
func get_damage() -> float:
	return weapon_data.damage if weapon_data else 0.0

func get_range() -> float:
	return weapon_data.attack_range if weapon_data else 0.0

func get_cooldown() -> float:
	return weapon_data.attack_cooldown if weapon_data else 0.0

## Blocking functionality
func can_block() -> bool:
	return weapon_data.can_block if weapon_data else false

func get_block_reduction() -> float:
	return weapon_data.block_reduction if weapon_data else 0.0
