class_name KickSystem

# Deduplication: each (A,B) pair resolved once per physics frame
static var _frame: int = -1
static var _pairs: Dictionary = {}

# Called by a flying kickable when it detects another kickable.
# Returns true if a merge happened (both objects will despawn).
static func resolve(owner: Node3D, other: Node3D, owner_vel: Vector3) -> bool:
	var f := Engine.get_physics_frames()
	if f != _frame:
		_frame = f
		_pairs.clear()

	var id_lo := mini(owner.get_instance_id(), other.get_instance_id())
	var id_hi := maxi(owner.get_instance_id(), other.get_instance_id())
	var key   := str(id_lo) + "_" + str(id_hi)
	if _pairs.has(key):
		return false
	_pairs[key] = true

	var other_vel: Vector3 = other.get("fly_vel") if other.get("fly_vel") != null else Vector3.ZERO
	var speed_a := Vector2(owner_vel.x, owner_vel.z).length()
	var speed_b := Vector2(other_vel.x, other_vel.z).length()
	var collision_speed := speed_a + speed_b

	# ── 1. Recipe merge (MergeRecipes) ────────────────────────────────────────
	var kt_a: String = owner.get("kickable_type") if owner.get("kickable_type") != null else ""
	var kt_b: String = other.get("kickable_type") if other.get("kickable_type") != null else ""
	if kt_a != "" and kt_b != "":
		var recipe: Dictionary = MergeRecipes.find(kt_a, kt_b, collision_speed)
		if not recipe.is_empty():
			_execute_recipe(owner, other, recipe)
			return true

	# ── 2. Merge (enemy-to-enemy) ─────────────────────────────────────────────
	if owner.has_method("can_merge_with") and owner.call("can_merge_with", other, collision_speed):
		owner.call("do_merge_with", other)
		return true

	# ── 3. Damage ─────────────────────────────────────────────────────────────
	var mod_a: float = owner.get("damage_modifier") if owner.get("damage_modifier") != null else 0.0
	var mod_b: float = other.get("damage_modifier") if other.get("damage_modifier") != null else 0.0

	if is_instance_valid(other) and other.has_method("apply_collision_damage") and mod_a > 0.0:
		other.call("apply_collision_damage", collision_speed * mod_a)
	if is_instance_valid(owner) and owner.has_method("apply_collision_damage") and mod_b > 0.0:
		owner.call("apply_collision_damage", collision_speed * mod_b)

	return false

static func _execute_recipe(a: Node3D, b: Node3D, recipe: Dictionary) -> void:
	var pos := (a.global_position + b.global_position) * 0.5
	var parent := a.get_parent()
	# Emit before freeing so respawn counters in Main decrement correctly.
	if a.has_signal("destroyed"):
		a.emit_signal("destroyed")
	if b.has_signal("destroyed"):
		b.emit_signal("destroyed")
	a.queue_free()
	b.queue_free()
	var scene: PackedScene = load(recipe["result_scene"])
	if scene == null or parent == null:
		return
	var result := scene.instantiate() as Node3D
	parent.add_child(result)
	result.global_position = pos
	FX.merge_smoke(pos + Vector3(0, 0.3, 0), parent)
	SFX.merge(parent)
