extends Node2D

# Unarmed punch - appears in front of player, low STR-based damage.
# Uses shade_spell_effects.png frames 108-113 for the punch, spawns damage_effect_punch on hit.

@export var damage: float = 3.0
@export var lifetime: float = 0.25
@export var punch_distance: float = 12.0 # Closer than sword (less range)

const PUNCH_FRAMES: Array = [108, 109, 110, 111, 112, 113]
const FRAME_DURATION: float = 0.04

var punch_direction: Vector2 = Vector2.RIGHT
var player_owner: Node = null
var hit_targets: Dictionary = {}
var elapsed: float = 0.0

var damage_effect_punch_scene: PackedScene = preload("res://scenes/damage_effect_punch.tscn")

@onready var sprite: Sprite2D = $Sprite2D
@onready var hit_area: Area2D = $Area2D

func _ready() -> void:
	if has_node("SfxSwosh"):
		$SfxSwosh.play()
	if hit_area and not hit_area.body_entered.is_connected(_on_body_entered):
		hit_area.body_entered.connect(_on_body_entered)
	if sprite and PUNCH_FRAMES.size() > 0:
		sprite.frame = PUNCH_FRAMES[0]

func setup(direction: Vector2, owner_player: Node, damage_value: float = 3.0) -> void:
	punch_direction = direction.normalized()
	player_owner = owner_player
	damage = damage_value
	rotation = punch_direction.angle()
	# Position in front of player
	if owner_player and is_instance_valid(owner_player):
		global_position = owner_player.global_position + punch_direction * punch_distance

func _process(delta: float) -> void:
	elapsed += delta
	# Keep punch in front of player
	if player_owner and is_instance_valid(player_owner):
		global_position = player_owner.global_position + punch_direction * punch_distance
	# Animate frames 108-113
	if sprite and PUNCH_FRAMES.size() > 0:
		var frame_idx = min(int(elapsed / FRAME_DURATION), PUNCH_FRAMES.size() - 1)
		sprite.frame = PUNCH_FRAMES[frame_idx]
	if elapsed >= lifetime:
		hit_area.set_deferred("monitoring", false)
		queue_free()

func _on_body_entered(body: Node2D) -> void:
	if body == player_owner:
		return
	if body in hit_targets:
		return
	hit_targets[body] = true

	# Only authority deals damage (same as sword_projectile)
	if player_owner and not player_owner.is_multiplayer_authority():
		return

	var attacker_pos = player_owner.global_position if player_owner and is_instance_valid(player_owner) else global_position

	# Ignore other players (no friendly fire) - pass through
	if body.is_in_group("player") and body.has_method("rpc_take_damage"):
		return

	# Enemy: apply damage (enemy's take_damage/rpc_take_damage applies defence)
	if body.is_in_group("enemy") and body.has_method("rpc_take_damage"):
		var game_world = get_tree().get_first_node_in_group("game_world")
		var enemy_name = body.name
		var enemy_index = body.get_meta("enemy_index") if body.has_meta("enemy_index") else -1
		if game_world and game_world.has_method("_request_enemy_damage"):
			if multiplayer.is_server():
				game_world._request_enemy_damage(enemy_name, enemy_index, damage, attacker_pos, false)
			else:
				game_world._request_enemy_damage.rpc_id(1, enemy_name, enemy_index, damage, attacker_pos, false)
		else:
			var enemy_peer_id = body.get_multiplayer_authority()
			if enemy_peer_id != 0:
				if multiplayer.is_server() and enemy_peer_id == multiplayer.get_unique_id():
					body.rpc_take_damage(damage, attacker_pos, false)
				else:
					body.rpc_take_damage.rpc_id(enemy_peer_id, damage, attacker_pos, false)
			else:
				body.rpc_take_damage.rpc(damage, attacker_pos, false)
		if has_node("SfxImpact"):
			$SfxImpact.play()
		_spawn_damage_effect(body.global_position)
		return

	# Interactables with health (boxes, etc.) - small damage
	if "health" in body:
		if has_node("SfxImpact"):
			$SfxImpact.play()
		body.health -= damage
		if body.health <= 0 and body.has_method("_break_into_pieces"):
			var game_world = get_tree().get_first_node_in_group("game_world")
			if game_world and multiplayer.has_multiplayer_peer():
				var obj_name = body.name
				if body.has_meta("object_index"):
					var idx = body.get_meta("object_index")
					if idx >= 0:
						obj_name = "InteractableObject_%d" % idx
				if multiplayer.is_server():
					game_world._rpc_to_ready_peers("_sync_object_break", [obj_name])
				else:
					game_world._sync_object_break.rpc_id(1, obj_name)
			body._break_into_pieces()
		_spawn_damage_effect(body.global_position)

func _spawn_damage_effect(at_position: Vector2) -> void:
	if multiplayer.has_multiplayer_peer() and player_owner and player_owner.is_multiplayer_authority():
		var game_world = get_tree().get_first_node_in_group("game_world")
		if game_world and game_world.has_method("_sync_punch_hit_effect"):
			if multiplayer.is_server():
				game_world._sync_punch_hit_effect(at_position.x, at_position.y)
				if game_world.has_method("_rpc_to_ready_peers"):
					game_world._rpc_to_ready_peers("_sync_punch_hit_effect", [at_position.x, at_position.y])
			else:
				game_world._request_punch_hit_effect.rpc_id(1, at_position.x, at_position.y)
		return
	if not damage_effect_punch_scene:
		return
	var effect = damage_effect_punch_scene.instantiate()
	var parent = get_parent()
	if parent:
		parent.add_child(effect)
	else:
		get_tree().current_scene.add_child(effect)
	effect.global_position = at_position
	if effect.has_method("setup"):
		effect.setup(at_position)
