extends Node

# A room authors its combat scenes. This queue follows real creatures, not
# lifetime kill counters: replaying a visit never announces an old victory.
const DIALOGUE := preload("res://scripts/core/dialogue_overlay.gd")
var enabled := DisplayServer.get_name() != "headless"
var _room_key := ""
var _queue: Array[Dictionary] = []
var _external_intros: Dictionary = {}
var _defeated: Dictionary = {}
var _room_cleared := false

func _ready() -> void:
	name = "CombatPresentation"
	process_mode = Node.PROCESS_MODE_ALWAYS

func reset() -> void:
	_room_key = DiveMap.current_room_id()
	_queue.clear()
	_external_intros.clear()
	_defeated.clear()
	_room_cleared = false

func room_entered() -> void:
	reset()
	if not _allowed():
		return
	for entry: Dictionary in DiveMap.current_room().get("combat_scenes", []):
		if entry.get("mode", "enemy") == "room" and GameState.conditions_met(entry.get("conditions", [])):
			_enqueue(str(entry.get("intro_scene", "")), str(entry.get("character", "")), true, str(entry.get("enemy", "")))

func room_cleared() -> void:
	if not _allowed() or _room_cleared:
		return
	_room_cleared = true
	for entry: Dictionary in DiveMap.current_room().get("combat_scenes", []):
		if entry.get("mode", "enemy") == "room" and GameState.conditions_met(entry.get("conditions", [])):
			_enqueue(str(entry.get("defeat_scene", "")), str(entry.get("character", "")), false, str(entry.get("enemy", "")))

func _process(_delta: float) -> void:
	if not enabled:
		return
	if _room_key != DiveMap.current_room_id():
		reset()
	var dialogue: Node = DIALOGUE.current
	if dialogue != null and is_instance_valid(dialogue):
		if not dialogue.has_meta(&"combat_presentation"):
			_external_intros[str(dialogue.get("scene_id"))] = true
		return
	_play_next()

func enemy_spawned(enemy: Enemy) -> void:
	if not _allowed():
		return
	var definition := _entry(enemy.enemy_id)
	if definition.is_empty():
		return
	# A phase can summon its successor and then flee/die in the same ability
	# chain. Let that chain finish so its aftermath precedes the new challenge.
	_enqueue(str(definition.get("intro_scene", "")), str(definition.get("character", "")), true, enemy.enemy_id, enemy.summon_generation > 0)

func enemy_defeated(enemy: Enemy) -> void:
	if not _allowed() or _defeated.has(enemy.get_instance_id()):
		return
	_defeated[enemy.get_instance_id()] = true
	var definition := _entry(enemy.enemy_id)
	if definition.is_empty():
		return
	# dismiss() also emits defeated. A bound summon disappearing is no win.
	var scene := ""
	if enemy.health <= 0.0:
		scene = str(definition.get("defeat_scene", ""))
	elif enemy.fleeing:
		scene = str(definition.get("flee_scene", ""))
	_enqueue(scene, str(definition.get("character", "")), false, enemy.enemy_id)

func _allowed() -> bool:
	return enabled and not GameState.sandbox_mode and not bool(get_parent().get("revisiting"))

func _entry(enemy_id: String) -> Dictionary:
	for entry: Dictionary in DiveMap.current_room().get("combat_scenes", []):
		if entry.get("mode", "enemy") == "enemy" and str(entry.get("enemy", "")) == enemy_id and GameState.conditions_met(entry.get("conditions", [])):
			return entry
	return {}

func _enqueue(scene: String, character: String, intro: bool, enemy: String, deferred: bool = false) -> void:
	if scene.is_empty() or not DataCatalog.STORY_SCENES.has(scene):
		return
	var entry := {"scene": scene, "character": character, "intro": intro, "enemy": enemy}
	if intro:
		_queue.append(entry)
	else:
		_queue.push_front(entry)
	if deferred:
		_play_next.call_deferred()
	else:
		_play_next()

func _play_next() -> void:
	if _queue.is_empty() or get_tree().paused or DIALOGUE.current != null:
		return
	while not _queue.is_empty():
		var entry: Dictionary = _queue.pop_front()
		# A dialogue can wake its own boss (Bob, Big Egg's offer, the Saule).
		# It already supplied that fight's introduction, including all its text.
		if entry.intro and _external_intros.has(entry.scene):
			_external_intros.erase(entry.scene)
			continue
		var overlay := DIALOGUE.play_scene(get_parent(), entry.scene, entry.character, entry.enemy)
		if overlay != null:
			overlay.set_meta(&"combat_presentation", true)
			overlay.connect("finished", func(_character: String, _scene: String) -> void: _play_next())
			return

func is_idle() -> bool:
	return _queue.is_empty()
