extends Area3D
class_name HitBox

## A component that deals damage to HurtBoxes
## Attach to weapons or attack effects
## Uses direct physics queries for reliable hit detection

signal hit_landed(target: Node, damage: float, knockback: float, attacker_pos: Vector3)

## Damage dealt on hit
@export var damage: float = 10.0
## Knockback force applied
@export var knockback: float = 5.0
## Owner entity (used to prevent self-damage and identify attacker)
@export var owner_entity: Node = null

## Global debug visibility toggle (press H in-game to toggle)
static var debug_visible: bool = false

## Whether hitbox is currently active (only deals damage when active)
var is_active: bool = false
## Tracks entities hit this attack (prevents multi-hit)
var _hits_this_attack: Array[Node] = []
## Shape for queries (extracted from child CollisionShape3D)
var _query_shape: Shape3D = null
## Debug mesh for visualization
var _debug_mesh: MeshInstance3D = null
var _debug_material: StandardMaterial3D = null

func _ready():
	# Find the collision shape for queries
	for child in get_children():
		if child is CollisionShape3D and child.shape:
			_query_shape = child.shape
			_create_debug_visualization(child)
			break

func _create_debug_visualization(collision_shape: CollisionShape3D):
	# Create a semi-transparent red mesh to visualize the hitbox
	_debug_mesh = MeshInstance3D.new()
	_debug_material = StandardMaterial3D.new()
	_debug_material.albedo_color = Color(1.0, 0.0, 0.0, 0.4)  # Red, semi-transparent
	_debug_material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
	_debug_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
	_debug_material.cull_mode = BaseMaterial3D.CULL_DISABLED  # Visible from both sides

	# Create mesh matching the collision shape
	var mesh: Mesh = null
	if collision_shape.shape is BoxShape3D:
		var box_mesh = BoxMesh.new()
		box_mesh.size = collision_shape.shape.size
		mesh = box_mesh
	elif collision_shape.shape is SphereShape3D:
		var sphere_mesh = SphereMesh.new()
		sphere_mesh.radius = collision_shape.shape.radius
		sphere_mesh.height = collision_shape.shape.radius * 2
		mesh = sphere_mesh
	elif collision_shape.shape is CapsuleShape3D:
		var capsule_mesh = CapsuleMesh.new()
		capsule_mesh.radius = collision_shape.shape.radius
		capsule_mesh.height = collision_shape.shape.height
		mesh = capsule_mesh

	if mesh:
		_debug_mesh.mesh = mesh
		_debug_mesh.material_override = _debug_material
		_debug_mesh.visible = HitBox.debug_visible
		# Don't set transform - it inherits from parent CollisionShape3D
		collision_shape.add_child(_debug_mesh)

func _physics_process(_delta):
	# Update debug visibility
	if _debug_mesh:
		_debug_mesh.visible = HitBox.debug_visible

	if not is_active:
		return

	_check_hits()

func _check_hits():
	if not _query_shape:
		# Fallback: create a default sphere
		var sphere = SphereShape3D.new()
		sphere.radius = 2.0
		_query_shape = sphere

	# Use physics server for reliable queries
	var space_state = get_world_3d().direct_space_state
	var query = PhysicsShapeQueryParameters3D.new()
	query.shape = _query_shape
	query.transform = global_transform
	query.collision_mask = 16  # Layer 5 (hurtbox)
	query.collide_with_areas = true
	query.collide_with_bodies = false

	var results = space_state.intersect_shape(query, 32)

	for result in results:
		var collider = result["collider"]
		if collider is HurtBox:
			_process_hit(collider)

func _process_hit(hurtbox: HurtBox):
	# Don't hit our own hurtbox
	if hurtbox.owner_entity == owner_entity:
		return

	# Don't hit same entity twice in one attack
	if hurtbox.owner_entity in _hits_this_attack:
		return

	# Enemies don't damage other enemies (only players)
	if owner_entity is BaseEnemy and hurtbox.owner_entity is BaseEnemy:
		return

	# Register this hit
	var target = hurtbox.owner_entity
	if target:
		_hits_this_attack.append(target)

	# Get attacker position for knockback direction
	var attacker_pos = global_position
	if owner_entity and owner_entity is Node3D:
		attacker_pos = owner_entity.global_position

	# Emit signal - let the weapon/owner handle damage routing to server
	hit_landed.emit(target, damage, knockback, attacker_pos)

## Activate hitbox (call when attack starts)
func activate():
	is_active = true
	_hits_this_attack.clear()
	# Change to yellow when active
	if _debug_material:
		_debug_material.albedo_color = Color(1.0, 1.0, 0.0, 0.5)  # Yellow, semi-transparent

## Deactivate hitbox (call when attack ends)
func deactivate():
	is_active = false
	_hits_this_attack.clear()
	# Change back to red when inactive
	if _debug_material:
		_debug_material.albedo_color = Color(1.0, 0.0, 0.0, 0.4)  # Red, semi-transparent

## Set damage stats (usually from weapon data)
func set_stats(new_damage: float, new_knockback: float):
	damage = new_damage
	knockback = new_knockback
