extends Node

# Server-authoritative registry of every connected peer's chosen loadout,
# replicated to all clients so everyone knows everyone's race/ship, not just
# their own.

signal loadout_updated(peer_id: int)
signal player_removed(peer_id: int, info: Dictionary)
# Fired once, the first time a peer ever registers a loadout (as opposed to
# loadout_updated, which also fires on every later ship re-pick) — lets
# ChatManager's admin announcer say "X has joined" exactly once per peer.
signal player_joined(peer_id: int)
# Fired only when an already-registered peer's race actually changes (e.g. a
# live team swap via the pause menu's SELECT TEAM), not on a fresh peer's
# first-ever loadout — lets BotManager rebalance the race the player left,
# which loadout_updated alone can't do since by the time it fires the old
# race is already overwritten in `players`.
signal race_changed(peer_id: int, old_race: int, new_race: int)

var players: Dictionary = {}  # peer_id -> {name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path}


func _ready() -> void:
	NetworkManager.peer_left.connect(_on_peer_left)


func get_local_id() -> int:
	return multiplayer.get_unique_id()


func get_info(peer_id: int) -> Dictionary:
	return players.get(peer_id, {})


func submit_local_loadout(player_name: String, race: int, ship_path: String, ship_scale: float, ship_speed_factor: float, role: String, ship_flame_path: String = "", ship_sound_path: String = "") -> void:
	submit_loadout.rpc(player_name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path)


# Server-only: registers a bot's loadout the same way a real player's
# submit_loadout would (minus the backfill, since a fresh bot has nothing to
# learn), broadcasting to every connected peer so bots render identically to
# human ships on every client. See bots/bot_manager.gd.
func register_bot(peer_id: int, bot_name: String, race: int, ship_path: String, ship_scale: float, ship_speed_factor: float, role: String, ship_flame_path: String = "", ship_sound_path: String = "") -> void:
	for pid in multiplayer.get_peers():
		_receive_loadout.rpc_id(pid, peer_id, bot_name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path)
	_store(peer_id, bot_name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path)


func unregister_bot(peer_id: int) -> void:
	_remove(peer_id)


# Client -> server (and locally to self via call_local). Server then relays
# the new loadout to everyone else, and backfills the newcomer with everyone
# else's already-known loadout.
@rpc("any_peer", "call_local", "reliable")
func submit_loadout(player_name: String, race: int, ship_path: String, ship_scale: float, ship_speed_factor: float, role: String, ship_flame_path: String = "", ship_sound_path: String = "") -> void:
	var sender_id := multiplayer.get_remote_sender_id()
	if sender_id == 0:
		sender_id = multiplayer.get_unique_id()

	if not multiplayer.is_server():
		_store(sender_id, player_name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path)
		return

	for peer_id in multiplayer.get_peers():
		if peer_id != sender_id:
			_receive_loadout.rpc_id(peer_id, sender_id, player_name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path)
	for peer_id in players:
		if peer_id != sender_id:
			var info: Dictionary = players[peer_id]
			_receive_loadout.rpc_id(sender_id, peer_id, info.name, info.race, info.ship_path, info.ship_scale, info.ship_speed_factor, info.role, info.get("ship_flame_path", ""), info.get("ship_sound_path", ""))

	_store(sender_id, player_name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path)


@rpc("authority", "call_remote", "reliable")
func _receive_loadout(peer_id: int, player_name: String, race: int, ship_path: String, ship_scale: float, ship_speed_factor: float, role: String, ship_flame_path: String = "", ship_sound_path: String = "") -> void:
	_store(peer_id, player_name, race, ship_path, ship_scale, ship_speed_factor, role, ship_flame_path, ship_sound_path)


func _store(peer_id: int, player_name: String, race: int, ship_path: String, ship_scale: float, ship_speed_factor: float, role: String, ship_flame_path: String = "", ship_sound_path: String = "") -> void:
	var old_race: int = players.get(peer_id, {}).get("race", 0)
	players[peer_id] = {
		"name": player_name,
		"race": race,
		"ship_path": ship_path,
		"ship_scale": ship_scale,
		"ship_speed_factor": ship_speed_factor,
		"role": role,
		"ship_flame_path": ship_flame_path,
		"ship_sound_path": ship_sound_path,
	}
	print("[PlayerRegistry] peer %d loadout: %s" % [peer_id, players[peer_id]])
	loadout_updated.emit(peer_id)
	if old_race == 0:
		player_joined.emit(peer_id)
	elif old_race != race:
		race_changed.emit(peer_id, old_race, race)


func _on_peer_left(peer_id: int) -> void:
	_remove(peer_id)


func _remove(peer_id: int) -> void:
	var info: Dictionary = players.get(peer_id, {})
	if players.erase(peer_id):
		player_removed.emit(peer_id, info)
