class_name ActionTargetResolver
extends RefCounted


func resolve(
	npc: SimNPC, origin: Vector3, simulation_manager: Node, active_world_adapter: Node
) -> Dictionary:
	var definition := SimulationDefinitions.get_action(npc.current_task)
	if definition == null:
		return {}
	match definition.target_type:
		SimulationIds.TARGET_RESOURCE:
			return _resolve_resource(
				npc, origin, definition, simulation_manager, active_world_adapter
			)
		SimulationIds.TARGET_ACTIVITY:
			return _resolve_activity(npc, origin, simulation_manager, active_world_adapter)
		SimulationIds.TARGET_FREE:
			return {
				"target_id": "", "position": origin + simulation_manager.get_wander_offset(npc.id)
			}
	return {}


func _resolve_resource(
	npc: SimNPC,
	origin: Vector3,
	definition: ActionDefinition,
	simulation_manager: Node,
	active_world_adapter: Node
) -> Dictionary:
	var best: Dictionary = {}
	var best_score := INF
	for candidate in active_world_adapter.get_resource_candidates(definition.resource_action_id):
		var node_id := StringName(candidate["target_id"])
		var state: ResourceStateRecord = simulation_manager.get_resource_state(node_id)
		if state == null or not state.can_npc_use() or not state.is_available_for(npc.id):
			continue
		var position: Vector3 = candidate["position"]
		var score := score_resource_candidate(npc, origin, position, state)
		if score < best_score:
			best_score = score
			best = candidate
	if best.is_empty():
		return {}
	var target_id := StringName(best["target_id"])
	if not simulation_manager.reserve_resource(target_id, npc.id):
		return {}
	return best


func _resolve_activity(
	npc: SimNPC, origin: Vector3, simulation_manager: Node, active_world_adapter: Node
) -> Dictionary:
	if not active_world_adapter.has_method("get_activity_candidates"):
		return active_world_adapter.get_activity_target(npc.current_task, origin)

	var best: Dictionary = {}
	var best_distance := INF
	var candidates: Array[Dictionary] = active_world_adapter.get_activity_candidates(npc.current_task)
	for candidate in candidates:
		var target_id := StringName(candidate["target_id"])
		var capacity := maxi(int(candidate.get("capacity", 1)), 1)
		if (
			simulation_manager.has_method("get_activity_target_claim_count")
			and simulation_manager.get_activity_target_claim_count(target_id, npc.id) >= capacity
		):
			continue
		var position: Vector3 = candidate["position"]
		var distance := origin.distance_squared_to(position)
		if distance < best_distance:
			best_distance = distance
			best = candidate

	if not best.is_empty():
		return best
	if not candidates.is_empty():
		return {}
	return active_world_adapter.get_activity_target(npc.current_task, origin)


func score_resource_candidate(
	npc: SimNPC, origin: Vector3, position: Vector3, state: ResourceStateRecord
) -> float:
	var distance := origin.distance_to(position)
	var comfort_overage := maxf(distance - state.get_comfort_distance(), 0.0)
	var risk_weight := 20.0 + maxf(100.0 - npc.energy, 0.0) * 0.2
	return (
		distance
		+ comfort_overage * 2.5
		+ state.get_safety_risk() * risk_weight
		- state.get_discovery_priority()
	)
