## HeadlessServer — Dedicated server entry point for headless mode.
##
## Loads the multiplayer-fps game scene DIRECTLY under /root/ (as a sibling,
## not a child), so RPC node paths match what clients expect.
##
## Usage:
##   godot --headless --scene res://scenes/headless_server.tscn
##   godot --headless --scene res://scenes/headless_server.tscn -- --port 34197
##
## Architecture:
##   This script runs as a temporary scene root. It loads multiplayer-fps.tscn
##   and adds it directly under get_tree().root so node paths (e.g.
##   /root/multiplayer-fps/Network/TeamManager) are identical on both
##   server and client.
##
##   1. Loads multiplayer-fps.tscn, adds to /root/
##   2. Removes Environment and UI
##   3. Starts ENet server
##   4. RCON admin console on configurable port

extends Node

## Default server port.
const DEFAULT_PORT: int = 34197

## Reference to the multiplayer-fps game scene (at root level).
var _game_scene: Node = null

# ---------------------------------------------------------------------------
# Lifecycle
# ---------------------------------------------------------------------------

func _ready() -> void:
	print("[HeadlessServer] Starting dedicated server...")

	var port: int = _parse_port()
	print("[HeadlessServer] Port: %d" % port)

	# Load the multiplayer-fps game scene
	var GameScene = load("res://examples/multiplayer-fps/multiplayer-fps.tscn")
	if GameScene == null:
		push_error("[HeadlessServer] Failed to load multiplayer-fps.tscn")
		get_tree().quit(1)
		return

	_game_scene = GameScene.instantiate()

	# CRITICAL: Add game scene directly to /root/ as a sibling, NOT as child.
	# This ensures RPC node paths are the same on both server and client.
	# Must use call_deferred because we're still in _ready().
	# Strip BEFORE adding to tree so queue_free works cleanly.
	_strip_for_headless()
	get_tree().root.call_deferred("add_child", _game_scene)

	# Start RCON admin console
	_start_rcon()

	# Set dedicated server flag on the player spawner to prevent
	# spawning a host avatar (peer 1) that would cause rollback errors.
	# Must be set before the ENet server starts (before on_server_start fires).
	var spawner = _game_scene.get_node_or_null("Network/Player Spawner")
	if spawner:
		spawner.spawn_host_avatar = false
		print("[HeadlessServer] Set spawn_host_avatar=false on PlayerSpawner")

	# Wait for game scene to enter the tree
	await get_tree().process_frame
	await get_tree().process_frame

	# Set current scene after it's safely in the tree
	get_tree().current_scene = _game_scene
	print("[HeadlessServer] Game scene added to root, current_scene updated")

	# Start the ENet server
	_start_server(port)

func _exit_tree() -> void:
	_stop_server()

# ---------------------------------------------------------------------------
# Strip scene for headless mode
# ---------------------------------------------------------------------------

## Remove nodes that are useless in headless mode: rendering, UI, audio.
func _strip_for_headless() -> void:
	if _game_scene == null:
		return

	# Remove Environment (sky, light, camera, world environment)
	var env = _game_scene.get_node_or_null("Environment")
	if env:
		env.queue_free()
		print("[HeadlessServer] Removed Environment node")

	# Remove UI (network popup, HUD, crosshair, etc.)
	var ui = _game_scene.get_node_or_null("UI")
	if ui:
		ui.queue_free()
		print("[HeadlessServer] Removed UI node")

	# Verify critical game logic nodes exist
	var network = _game_scene.get_node_or_null("Network")
	if network == null:
		push_error("[HeadlessServer] Missing Network node in game scene!")
		return

	for required in ["Player Spawner", "TeamManager", "RoundManager", "EconomyManager", "Bomb"]:
		if network.get_node_or_null(required) == null:
			push_warning("[HeadlessServer] Missing Network/%s — some features disabled" % required)

	print("[HeadlessServer] Scene stripped for headless mode")

# ---------------------------------------------------------------------------
# Server startup
# ---------------------------------------------------------------------------

func _start_server(port: int) -> void:
	# Create ENet peer directly
	var peer = ENetMultiplayerPeer.new()
	var err = peer.create_server(port)
	if err != OK:
		push_error("[HeadlessServer] Failed to create ENet server on port %d: %s" % [port, error_string(err)])
		get_tree().quit(1)
		return

	get_tree().get_multiplayer().multiplayer_peer = peer
	get_tree().get_multiplayer().server_relay = true

	print("[HeadlessServer] ENet server started on port %d" % port)

	# Wait for NetworkEvents to fire on_server_start
	await get_tree().process_frame

	# NetworkEvents handles on_server_start -> player-spawner -> round_manager
	if NetworkEvents and NetworkEvents.enabled:
		print("[HeadlessServer] NetworkEvents active — waiting for on_server_start")
	else:
		print("[HeadlessServer] NetworkEvents not available — manually bootstrapping")
		_manual_startup()

func _stop_server() -> void:
	var mp = get_tree().get_multiplayer()
	if mp and mp.multiplayer_peer:
		mp.multiplayer_peer.close()
		mp.multiplayer_peer = null
	print("[HeadlessServer] Server stopped")

## Fallback startup when NetworkEvents isn't available.
func _manual_startup() -> void:
	if Engine.has_singleton("NetworkTime"):
		var nt = Engine.get_singleton("NetworkTime")
		if nt and nt.has_method("start"):
			nt.start()
			print("[HeadlessServer] NetworkTime started manually")

	var network = _game_scene.get_node_or_null("Network") if _game_scene else null
	if network == null:
		return

	var spawner = network.get_node_or_null("Player Spawner")
	if spawner:
		if spawner.has_method("_handle_host"):
			spawner._handle_host()
			print("[HeadlessServer] PlayerSpawner._handle_host() called")

	if Engine.has_singleton("NetworkEvents"):
		var ne = Engine.get_singleton("NetworkEvents")
		if ne and ne.has_signal("on_server_start"):
			ne.on_server_start.emit()
			print("[HeadlessServer] Emitted NetworkEvents.on_server_start")
		if ne and ne.has_signal("on_peer_join"):
			ne.on_peer_join.emit(1)
			print("[HeadlessServer] Emitted NetworkEvents.on_peer_join(1)")

	var round_mgr = network.get_node_or_null("RoundManager")
	if round_mgr:
		if round_mgr.has_method("start_match"):
			round_mgr.call_deferred("start_match")
			print("[HeadlessServer] RoundManager.start_match() called")
		elif round_mgr.has_method("start_round"):
			round_mgr.call_deferred("start_round")
			print("[HeadlessServer] RoundManager.start_round() called")

	print("[HeadlessServer] Manual startup complete")

# ---------------------------------------------------------------------------
# RCON admin console
# ---------------------------------------------------------------------------

func _start_rcon() -> void:
	var rcon_path = "res://scripts/rcon/rcon_server.gd"
	if not ResourceLoader.exists(rcon_path):
		print("[HeadlessServer] RCON script not found — skipping")
		return

	var RconServerClass = load(rcon_path)
	if RconServerClass == null:
		return

	var rcon = RconServerClass.new()
	rcon.name = "RconServer"
	rcon.enabled = true
	rcon.port = _parse_rcon_port()
	rcon.password = _parse_rcon_password()
	add_child(rcon)
	print("[HeadlessServer] RCON admin console started on port %d" % rcon.port)

func _parse_rcon_port() -> int:
	var args = OS.get_cmdline_args()
	for i in range(args.size()):
		if args[i] == "--rcon-port" and i + 1 < args.size():
			return clampi(args[i + 1].to_int(), 1024, 65535)
	if OS.has_environment("RCON_PORT"):
		return clampi(OS.get_environment("RCON_PORT").to_int(), 1024, 65535)
	return 28960

func _parse_rcon_password() -> String:
	var args = OS.get_cmdline_args()
	for i in range(args.size()):
		if args[i] == "--rcon-password" and i + 1 < args.size():
			return args[i + 1]
	if OS.has_environment("RCON_PASSWORD"):
		return OS.get_environment("RCON_PASSWORD")
	var pw_path = "res://config/rcon_password.cfg"
	if ResourceLoader.exists(pw_path):
		var f = FileAccess.open(pw_path, FileAccess.READ)
		if f:
			var line = f.get_line().strip_edges()
			f.close()
			if not line.is_empty() and not line.begins_with("#"):
				return line
	return "changeme"

# ---------------------------------------------------------------------------
# Port parsing
# ---------------------------------------------------------------------------

func _parse_port() -> int:
	var user_args = OS.get_cmdline_user_args()
	for i in range(user_args.size()):
		if user_args[i] == "--port" and i + 1 < user_args.size():
			var p = user_args[i + 1].to_int()
			if p > 0 and p < 65536:
				return p

	var args = OS.get_cmdline_args()
	for i in range(args.size()):
		if args[i] == "--port" and i + 1 < args.size():
			var p = args[i + 1].to_int()
			if p > 0 and p < 65536:
				return p

	if OS.has_environment("SERVER_PORT"):
		var p = OS.get_environment("SERVER_PORT").to_int()
		if p > 0 and p < 65536:
			return p

	return DEFAULT_PORT
