extends CanvasLayer

const DEBUG_LOGS := false

@export var simulation_manager: Node

@onready var village_stats_label: Label = $VillagePanel/MarginContainer/VillageStatsLabel
@onready var npc_inspector_label: Label = $NpcInspectorPanel/MarginContainer/NpcInspectorLabel

var selected_npc_index := 0
var inspector_refresh_accumulator := 0.0


func _ready() -> void:
	if simulation_manager == null:
		push_error("VillageUI: simulation_manager missing")
		return

	if simulation_manager.has_signal("village_changed"):
		simulation_manager.village_changed.connect(_on_village_changed)
	else:
		push_error("VillageUI: SimulationManager has no village_changed signal")
	if simulation_manager.has_signal("npc_decision_recorded"):
		simulation_manager.npc_decision_recorded.connect(_on_npc_decision_recorded)
	else:
		push_error("VillageUI: SimulationManager has no npc_decision_recorded signal")
	if simulation_manager.has_signal("state_restored"):
		simulation_manager.state_restored.connect(_on_state_restored)

	if "village" in simulation_manager:
		_on_village_changed(simulation_manager.village)
	_refresh_npc_inspector()


func _process(delta: float) -> void:
	inspector_refresh_accumulator += delta
	if inspector_refresh_accumulator < 0.25:
		return
	inspector_refresh_accumulator = 0.0
	_refresh_npc_inspector()


func _unhandled_key_input(event: InputEvent) -> void:
	if not event is InputEventKey or not event.pressed or event.echo:
		return
	if event.keycode != KEY_TAB or simulation_manager.npcs.is_empty():
		return
	selected_npc_index = (selected_npc_index + 1) % simulation_manager.npcs.size()
	_refresh_npc_inspector()
	get_viewport().set_input_as_handled()


func _on_village_changed(village: SimVillage) -> void:
	village_stats_label.text = (
		"""
	Village

	Food: %s
	Wood: %s
	Safety: %s
	Knowledge: %s
	Starving: %s

	Food Mod: %.2f
	Safety Mod: %.2f
	Knowledge Mod: %.2f
	"""
		% [
			round(village.food),
			round(village.wood),
			round(village.safety),
			round(village.knowledge),
			simulation_manager.get_starving_count(),
			village.food_modifier,
			village.safety_modifier,
			village.knowledge_modifier
		]
	)


func _on_npc_decision_recorded(_npc: SimNPC, _decision: ActionSelectionResult) -> void:
	_refresh_npc_inspector()


func _on_state_restored() -> void:
	selected_npc_index = 0
	_refresh_npc_inspector()


func _refresh_npc_inspector() -> void:
	if simulation_manager == null or simulation_manager.npcs.is_empty():
		npc_inspector_label.text = "No villagers available"
		return
	selected_npc_index = clampi(selected_npc_index, 0, simulation_manager.npcs.size() - 1)
	var npc: SimNPC = simulation_manager.npcs[selected_npc_index]
	var profession_definition := SimulationDefinitions.get_profession(npc.profession)
	var profession_name := String(npc.profession)
	if profession_definition != null:
		profession_name = profession_definition.display_name
	var action_name := _get_action_name(npc.current_task)
	var destination_name := "Resolving"
	if not npc.target_id.is_empty():
		destination_name = String(npc.target_id)
	var decision: ActionSelectionResult = simulation_manager.get_latest_decision(npc.id)
	var reason_text := "Awaiting the next decision"
	var score_text := ""
	if decision != null:
		reason_text = decision.reason
		var score_keys: Array = decision.scores.keys()
		score_keys.sort()
		var score_rows: Array[String] = []
		for action_id in score_keys:
			score_rows.append(
				"%s %.2f" % [_get_action_name(StringName(action_id)), float(decision.scores[action_id])]
			)
		if not score_rows.is_empty():
			score_text = "\n\nUtility\n" + "\n".join(score_rows)
	npc_inspector_label.text = (
		"%s  ·  %s\n"
		+ "Task: %s (%s)\n"
		+ "Destination: %s\n"
		+ "Hunger: %.0f   Energy: %.0f\n"
		+ "Carrying food: %.0f\n\n"
		+ "Why\n%s%s\n\n"
		+ "[Tab] Next villager"
	) % [
		npc.npc_name,
		profession_name,
		action_name,
		npc.task_state,
		destination_name,
		npc.hunger,
		npc.energy,
		npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD),
		reason_text,
		score_text
	]


func _get_action_name(action_id: StringName) -> String:
	if action_id == SimulationIds.ACTION_IDLE:
		return "Idle"
	if action_id == SimulationIds.ACTION_DEAD:
		return "Dead"
	var definition := SimulationDefinitions.get_action(action_id)
	if definition != null:
		return definition.display_name
	return String(action_id).capitalize()
