extends Node

const DEBUG_LOGS := true

@export var npc_visual_scene: PackedScene
@export var simulation_manager: Node
@export var active_npcs_parent: Node3D

var active_npc_visuals := {}


func debug_log(message: String) -> void:
	if DEBUG_LOGS:
		print("[WorldViewManager] ", message)


func _ready() -> void:
	call_deferred("initialize_world_view")


func initialize_world_view() -> void:
	await get_tree().physics_frame
	if simulation_manager.has_signal("npc_target_requested"):
		simulation_manager.npc_target_requested.connect(_on_npc_target_requested)
	else:
		push_error("WorldViewManager: missing npc_target_requested signal")
	if simulation_manager.has_signal("npc_travel_requested"):
		simulation_manager.npc_travel_requested.connect(_on_npc_travel_requested)
	else:
		push_error("WorldViewManager: missing npc_travel_requested signal")
	if simulation_manager.has_signal("npc_died"):
		simulation_manager.npc_died.connect(_on_npc_died)
	else:
		push_error("WorldViewManager: SimulationManager has no npc_died signal")
	if simulation_manager.has_signal("npc_inventory_changed"):
		simulation_manager.npc_inventory_changed.connect(_on_npc_inventory_changed)
	else:
		push_error("WorldViewManager: SimulationManager has no npc_inventory_changed signal")
	if simulation_manager.has_signal("state_restored"):
		simulation_manager.state_restored.connect(_on_simulation_state_restored)
	else:
		push_error("WorldViewManager: SimulationManager has no state_restored signal")
	spawn_initial_npcs()


func spawn_initial_npcs() -> void:
	if simulation_manager == null:
		push_error("WorldViewManager: simulation_manager is missing")
		return
	if npc_visual_scene == null:
		push_error("WorldViewManager: npc_visual_scene is missing")
		return
	if active_npcs_parent == null:
		push_error("WorldViewManager: active_npcs_parent is missing")
		return
	debug_log("Spawning NPC visuals. Count: %s" % simulation_manager.npcs.size())
	for npc in simulation_manager.npcs:
		spawn_npc_visual(npc)


func spawn_npc_visual(npc: SimNPC) -> void:
	if active_npc_visuals.has(npc.id):
		return
	var visual = npc_visual_scene.instantiate()
	active_npcs_parent.add_child(visual)
	visual.global_position = npc.position
	visual.setup_from_sim(npc)
	if visual.has_signal("arrived_at_target"):
		visual.arrived_at_target.connect(_on_npc_visual_arrived)
	else:
		push_error("WorldViewManager: NPCVisual has no arrived_at_target signal")
	if visual.has_signal("navigation_failed"):
		visual.navigation_failed.connect(_on_npc_visual_navigation_failed)
	else:
		push_error("WorldViewManager: NPCVisual has no navigation_failed signal")
	if visual.has_signal("position_changed"):
		visual.position_changed.connect(_on_npc_visual_position_changed)
	else:
		push_error("WorldViewManager: NPCVisual has no position_changed signal")
	active_npc_visuals[npc.id] = visual
	debug_log("Spawned visual for %s at %s" % [npc.npc_name, npc.position])
	if npc.is_dead:
		visual.apply_dead_visual_state()
	elif npc.task_state == SimNPC.TASK_STATE_TRAVELING:
		simulation_manager.request_current_travel(npc.id)


func despawn_npc_visual(npc_id: int) -> bool:
	var visual = active_npc_visuals.get(npc_id) as Node3D
	if visual == null:
		return false
	simulation_manager.synchronize_npc_position(npc_id, visual.global_position)
	active_npc_visuals.erase(npc_id)
	visual.queue_free()
	return true


func reload_npc_visual(npc_id: int) -> bool:
	if active_npc_visuals.has(npc_id):
		return false
	for npc in simulation_manager.npcs:
		if npc.id == npc_id:
			spawn_npc_visual(npc)
			return true
	return false


func _on_npc_target_requested(npc: SimNPC) -> void:
	var visual = active_npc_visuals.get(npc.id) as Node3D
	if visual == null:
		return
	if not simulation_manager.resolve_npc_target(npc.id, visual.global_position):
		debug_log("Target resolution failed for %s" % npc.npc_name)


func _on_npc_travel_requested(npc: SimNPC, target_position: Vector3) -> void:
	var visual = active_npc_visuals.get(npc.id)
	if visual == null:
		return
	visual.set_target_position(target_position)


func _on_npc_visual_arrived(sim_id: int) -> void:
	debug_log("NPC visual arrived. sim_id=%s" % sim_id)
	if simulation_manager == null:
		push_error("WorldViewManager: simulation_manager missing during arrival")
		return
	var visual = active_npc_visuals.get(sim_id) as Node3D
	if visual != null:
		simulation_manager.synchronize_npc_position(sim_id, visual.global_position)
	simulation_manager.notify_npc_arrived(sim_id)


func _on_npc_visual_navigation_failed(sim_id: int) -> void:
	debug_log("NPC visual navigation failed. sim_id=%s" % sim_id)
	if simulation_manager == null:
		push_error("WorldViewManager: simulation_manager missing during failure")
		return
	var visual = active_npc_visuals.get(sim_id) as Node3D
	if visual != null:
		simulation_manager.synchronize_npc_position(sim_id, visual.global_position)
	simulation_manager.notify_npc_navigation_failed(sim_id)


func _on_npc_visual_position_changed(sim_id: int, active_position: Vector3) -> void:
	simulation_manager.synchronize_npc_position(sim_id, active_position)


func _on_npc_died(npc: SimNPC) -> void:
	if not active_npc_visuals.has(npc.id):
		return
	var visual = active_npc_visuals[npc.id]
	if visual.has_method("apply_dead_visual_state"):
		visual.apply_dead_visual_state()
	else:
		push_error("WorldViewManager: NPCVisual has no apply_dead_visual_state")


func _on_npc_inventory_changed(npc: SimNPC, item_id: StringName, amount: float) -> void:
	if item_id != SimulationIds.RESOURCE_FOOD:
		return
	var visual = active_npc_visuals.get(npc.id)
	if visual == null:
		return
	if visual.has_method("set_carried_food_visible"):
		visual.set_carried_food_visible(amount > 0.0)
	else:
		push_error("WorldViewManager: NPCVisual has no set_carried_food_visible method")


func _on_simulation_state_restored() -> void:
	for visual in active_npc_visuals.values():
		visual.queue_free()
	active_npc_visuals.clear()
	spawn_initial_npcs()
