## MinionManager — 玩家召唤物管理（Autoload: MinionManager）
## 权威来源：architecture_design.md §5.12 + ADR-R4-N4（独立管理器，方案 B）
## 数量上限小（MAX_MINIONS=20，ADR-R4-N4/plan S5），不需 C# 热路径，用 Array[Dictionary]
## 炮台型（Stationary）AI：定点对范围内最近敌人开火
##
## 注：plan S5 提到 "SoA stride=8"，但 §5.12 权威实现明确用 Array[Dictionary]
##    且 "不需要 C# 热路径"，故按 §5.12 实现；SoA 化为后续可选优化。
extends Node

const MAX_MINIONS:     int   = 20    # ADR-R4-N4：同场最大召唤物，超出 FIFO 顶替最旧
const DEFAULT_LIFETIME: float = 30.0

## 活跃召唤物：{ id, owner_id, pos, lifetime_rem, range, fire_interval, fire_cd,
##              damage, bullet_speed, bullet_radius, bullet_lifetime, damage_type }
var _minions: Array = []
var _next_id: int = 0

## def: Dictionary（项目无 .tres，用字典描述召唤物面板）
##   可选键：lifetime, range, fire_interval, damage, bullet_speed, bullet_radius,
##           bullet_lifetime, damage_type
func spawn_minion(def: Dictionary, owner_id: int, pos: Vector2) -> int:
	if _minions.size() >= MAX_MINIONS:
		_expire(_minions[0]["id"])  # FIFO：顶掉最旧
	var lifetime: float = float(def.get("lifetime", DEFAULT_LIFETIME))
	if lifetime <= 0.0:
		lifetime = DEFAULT_LIFETIME
	var id: int = _next_id
	_next_id = (_next_id + 1) % 100000
	var entry: Dictionary = {
		"id":            id,
		"owner_id":      owner_id,
		"pos":           pos,
		"lifetime_rem":  lifetime,
		"range":         float(def.get("range", 350.0)),
		"fire_interval": maxf(0.05, float(def.get("fire_interval", 0.8))),
		"fire_cd":       0.0,
		"damage":        float(def.get("damage", 4.0)),
		"bullet_speed":  float(def.get("bullet_speed", 360.0)),
		"bullet_radius": float(def.get("bullet_radius", 5.0)),
		"bullet_lifetime": float(def.get("bullet_lifetime", 2.0)),
		"damage_type":   int(def.get("damage_type", 0)),
	}
	_minions.append(entry)
	EventBus.emit(EventID.MINION_SPAWNED, {
		"minion_id": id, "owner_id": owner_id, "current_count": _minions.size(),
	})
	return id

func _physics_process(delta: float) -> void:
	var i: int = 0
	while i < _minions.size():
		var m: Dictionary = _minions[i]
		m["lifetime_rem"] -= delta
		if m["lifetime_rem"] <= 0.0:
			_expire(m["id"])
			continue  # 不自增 i：当前槽已被 remove_at 后移的元素填补
		# 炮台 AI：冷却到点且范围内有敌人则朝最近敌人开火
		m["fire_cd"] -= delta
		if m["fire_cd"] <= 0.0:
			var pos: Vector2 = m["pos"]
			var rng: float = m["range"]
			if SpatialGrid.query_circle(pos, rng).size() > 0:
				var tgt: Vector2 = EnemyManager.get_nearest_pos(pos, rng)
				var dir: Vector2 = (tgt - pos)
				dir = dir.normalized() if dir.length() > 0.001 else Vector2.RIGHT
				BulletManager.spawn_bullet(
					pos, dir * m["bullet_speed"], m["bullet_lifetime"],
					m["bullet_radius"], m["damage"], 1.0, m["damage_type"],
					m["owner_id"], 0, 0.0, {})
				m["fire_cd"] = m["fire_interval"]
		i += 1

func _expire(minion_id: int) -> void:
	for i in _minions.size():
		if _minions[i]["id"] == minion_id:
			var owner: int = _minions[i]["owner_id"]
			_minions.remove_at(i)
			EventBus.emit(EventID.MINION_EXPIRED, {
				"minion_id": minion_id, "owner_id": owner, "current_count": _minions.size(),
			})
			return

func recall_all(owner_id: int = -1) -> void:
	# owner_id<0 → 全部召回（Run 结束）；否则仅该 owner
	var ids: Array = []
	for m in _minions:
		if owner_id < 0 or m["owner_id"] == owner_id:
			ids.append(m["id"])
	for mid in ids:
		_expire(mid)

func get_count(owner_id: int = -1) -> int:
	if owner_id < 0:
		return _minions.size()
	var c: int = 0
	for m in _minions:
		if m["owner_id"] == owner_id:
			c += 1
	return c

func reset() -> void:
	_minions.clear()
	_next_id = 0
