extends Node3D
class_name BaseWeapon

## Base class for equipped weapons
## Attached to player's hand via BoneAttachment3D
## Uses HitBox/HurtBox system for damage detection

signal attack_performed()
signal hit_connected(target: Node)

@export var weapon_data: WeaponData

# Runtime references
var owner_character: Node = null  # Can be Character or ArmedEnemy
var _mesh_instance: Node3D = null
var _attack_timer: float = 0.0
var _hitbox: HitBox = null
var _is_attacking: bool = false  # Prevents overlapping attacks

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

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)

	# Check if mesh has a HitBox child, use it instead of auto-generated one
	var mesh_hitbox = _mesh_instance.get_node_or_null("HitBox")
	if mesh_hitbox and mesh_hitbox is HitBox:
		# Use the hitbox from the mesh scene
		print("[BaseWeapon] Found manual HitBox in mesh scene")
		if _hitbox:
			_hitbox.queue_free()
		_hitbox = mesh_hitbox
		_configure_hitbox()
	else:
		print("[BaseWeapon] No manual HitBox found, will auto-generate")

## Setup the hitbox for this weapon
func _setup_hitbox():
	# Skip if we already have a hitbox (e.g., from mesh scene)
	if _hitbox:
		return

	# Create hitbox dynamically based on weapon range
	_hitbox = HitBox.new()
	_hitbox.name = "HitBox"

	# Add collision shape BEFORE adding hitbox to tree (so _ready can find it)
	var collision = CollisionShape3D.new()
	var sphere = SphereShape3D.new()
	var range_val = weapon_data.attack_range if weapon_data else 1.5
	sphere.radius = range_val
	collision.shape = sphere
	_hitbox.add_child(collision)

	# Now add the fully configured hitbox to the scene
	add_child(_hitbox)

	_configure_hitbox()

## Configure hitbox with weapon stats and owner
func _configure_hitbox():
	if not _hitbox:
		return

	# Set damage stats from weapon
	if weapon_data:
		_hitbox.set_stats(weapon_data.damage, weapon_data.knockback_force)

	# Set owner to prevent self-damage
	_hitbox.owner_entity = owner_character

	# Connect to hit signal
	if not _hitbox.hit_landed.is_connected(_on_hitbox_hit):
		_hitbox.hit_landed.connect(_on_hitbox_hit)

## Called when hitbox connects with a hurtbox
func _on_hitbox_hit(target: Node, damage_amount: float, knockback_amount: float, attacker_pos: Vector3):
	if not target or not owner_character:
		return

	# Flash the target's hurtbox red for visual feedback
	if target is Node:
		var hurtbox = target.find_child("HurtBox", true, false)
		if hurtbox and hurtbox.has_method("flash_hit"):
			hurtbox.flash_hit()

	hit_connected.emit(target)

	# Route damage through server
	var attacker_id = multiplayer.get_unique_id()

	# Check if owner has _server_apply_damage (Character has it, ArmedEnemy doesn't)
	if owner_character.has_method("_server_apply_damage"):
		if multiplayer.is_server():
			# We are server, apply directly
			owner_character._server_apply_damage(
				target.name,
				damage_amount,
				attacker_id,
				knockback_amount,
				attacker_pos
			)
		else:
			# Send to server
			owner_character.rpc_id(1, "_server_apply_damage",
				target.name,
				damage_amount,
				attacker_id,
				knockback_amount,
				attacker_pos
			)
	else:
		# ArmedEnemy or other entity - apply damage directly if we're server
		if multiplayer.is_server() and target is BaseUnit:
			target.take_damage(damage_amount, 1, knockback_amount, attacker_pos)

## 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 and if already attacking
	if _attack_timer > 0 or _is_attacking:
		return false

	# Calculate total attack duration (startup + active)
	var startup = weapon_data.startup_time if weapon_data else 0.15
	var active = weapon_data.active_time if weapon_data else 0.2
	var total_duration = startup + active

	# Set cooldown to at least cover the full attack duration
	var cooldown = max(weapon_data.attack_cooldown, total_duration)
	_attack_timer = cooldown
	_is_attacking = true

	# Notify owner character of attack cooldown (for UI) - only for Characters
	if owner_character.is_multiplayer_authority() and "_attack_timer" in owner_character:
		owner_character._attack_timer = cooldown

	# Play attack animation on owner (use weapon's animation)
	if "_body" in owner_character and owner_character._body:
		var anim_name = weapon_data.attack_animation if weapon_data.attack_animation else "Attack_OneHand"
		if owner_character._body.has_method("play_attack"):
			owner_character._body.play_attack(anim_name)
		# Sync animation to other clients if method exists
		if owner_character.has_method("_sync_attack_animation"):
			owner_character._sync_attack_animation.rpc(anim_name)

	# Activate hitbox for the attack duration
	# For players: only activate on authority
	# For enemies: they are server-authoritative, so check if we're server
	var should_activate = owner_character.is_multiplayer_authority() or multiplayer.is_server()
	if should_activate:
		_activate_hitbox()

	attack_performed.emit()
	return true

## Activate the hitbox for attack detection - waits for startup, then activates
func _activate_hitbox():
	if not _hitbox:
		_is_attacking = false
		return

	# Update owner reference in case it changed
	_hitbox.owner_entity = owner_character

	# STARTUP PHASE - Wait before activating (wind-up animation)
	var startup = weapon_data.startup_time if weapon_data else 0.15
	if startup > 0:
		await get_tree().create_timer(startup).timeout

	# Check if weapon/hitbox is still valid after await
	if not _hitbox or not is_instance_valid(_hitbox):
		_is_attacking = false
		return

	# ACTIVE PHASE - Hitbox on, can deal damage
	_hitbox.activate()

	# Wait for active duration
	var active = weapon_data.active_time if weapon_data else 0.2
	await get_tree().create_timer(active).timeout

	# RECOVERY PHASE - Hitbox off
	if _hitbox and is_instance_valid(_hitbox):
		_hitbox.deactivate()

	# Attack complete
	_is_attacking = false

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

## Set the character who owns this weapon (can be Character or ArmedEnemy)
func set_owner_character(character: Node):
	owner_character = character
	# Update hitbox owner
	if _hitbox:
		_hitbox.owner_entity = 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
