extends Node3D

const PLAYER_SCENE: PackedScene = preload("res://scenes/entities/player/player.tscn")

@onready var players_root: Node3D = $EntityRoot/Players
@onready var ground_shape: CollisionShape3D = $Ground/GroundShape
@onready var npc_system: Node = $Systems/NpcSystem
@onready var wave_system: Node = $Systems/WaveSystem

func _ready() -> void:
  add_to_group("world")
  _setup_ground_collision()
  Net.world_ready.connect(_on_world_ready)
  Net.peer_world_loaded.connect(_on_peer_world_loaded)
  if Net.is_host():
    multiplayer.peer_connected.connect(_on_peer_connected)
    multiplayer.peer_disconnected.connect(_on_peer_disconnected)
  Net.reset_world_ready()
  Net.mark_world_loaded()

func _exit_tree() -> void:
  if Net.is_host():
    if multiplayer.peer_connected.is_connected(_on_peer_connected):
      multiplayer.peer_connected.disconnect(_on_peer_connected)
    if multiplayer.peer_disconnected.is_connected(_on_peer_disconnected):
      multiplayer.peer_disconnected.disconnect(_on_peer_disconnected)
  if Net.world_ready.is_connected(_on_world_ready):
    Net.world_ready.disconnect(_on_world_ready)
  if Net.peer_world_loaded.is_connected(_on_peer_world_loaded):
    Net.peer_world_loaded.disconnect(_on_peer_world_loaded)

func _setup_ground_collision() -> void:
  var shape := WorldBoundaryShape3D.new()
  shape.plane = Plane(Vector3.UP, 0.0)
  ground_shape.shape = shape

func _on_world_ready() -> void:
  if not Net.is_host():
    return
  _spawn_player(1)
  for id in multiplayer.get_peers():
    _spawn_player(id)
  npc_system.spawn_default_npcs()
  if wave_system and wave_system.has_method("start_wave"):
    wave_system.start_wave(GameState.current_wave)

func _on_peer_connected(id: int) -> void:
  Net.tell_peer_to_load_scene(id, GameState.SCENE_WORLD)

func _on_peer_world_loaded(peer_id: int) -> void:
  if not Net.is_host():
    return
  _spawn_player(peer_id)

func _on_peer_disconnected(id: int) -> void:
  var node := players_root.get_node_or_null(str(id))
  if node:
    node.queue_free()

func _spawn_player(peer_id: int) -> void:
  if players_root.get_node_or_null(str(peer_id)) != null:
    return
  var p: CharacterBody3D = PLAYER_SCENE.instantiate()
  p.name = str(peer_id)
  var spawn := _spawn_position_for(peer_id)
  players_root.add_child(p, true)
  p.global_position = spawn

func _spawn_position_for(peer_id: int) -> Vector3:
  if GameState.portal_return_position != Vector3.ZERO:
    var off: float = float(peer_id) * 0.7
    return GameState.portal_return_position + Vector3(cos(off) * 1.2, 1.5, sin(off) * 1.2)
  var angle: float = wrapf(float(peer_id) * 1.7, 0.0, TAU)
  return Vector3(cos(angle) * 5.0, 1.5, sin(angle) * 5.0)
