extends SceneTree


class FakeActiveWorld:
	extends Node

	var resource_candidates: Array[Dictionary] = []
	var activity_position := Vector3(4.0, 0.0, 7.0)

	func get_resource_candidates(_action_id: StringName) -> Array[Dictionary]:
		return resource_candidates

	func get_activity_target(_action_id: StringName) -> Dictionary:
		return {"target_id": "", "position": activity_position}


var failures: Array[String] = []
var travel_requests: Array[Vector3] = []


func _initialize() -> void:
	call_deferred("_run")


func _run() -> void:
	_test_selection_and_execution_are_separate()
	_test_target_resolution_and_travel_are_separate()

	if failures.is_empty():
		print("[TEST] Action system boundaries passed")
		quit(0)
		return

	for failure in failures:
		push_error("[TEST] " + failure)
	quit(1)


func _test_selection_and_execution_are_separate() -> void:
	var village := SimVillage.new()
	village.debug_logs = false
	var npc := SimNPC.new(700, "BoundaryWorker", SimulationIds.PROFESSION_FARMER, 5.0, 5.0)
	npc.hunger = 80.0
	var executor := ActionExecutionSystem.new()
	executor.advance_npc(npc, village)
	_check(
		npc.current_task == SimulationIds.ACTION_IDLE,
		"Execution should update state without selecting an action"
	)
	var selector := ActionSelectionSystem.new()
	var selection := selector.select_action(npc, village)
	_check(
		selection.action_id == SimulationIds.ACTION_WITHDRAW_FOOD,
		"Selection should independently choose food retrieval"
	)


func _test_target_resolution_and_travel_are_separate() -> void:
	var adapter := FakeActiveWorld.new()
	adapter.resource_candidates = [
		{"target_id": "boundary_bush", "position": Vector3(3.0, 0.0, 2.0)}
	]
	root.add_child(adapter)

	var manager: Node = load("res://simulation/SimulationManager.gd").new()
	manager.debug_logs = false
	manager.active_world_adapter = adapter
	root.add_child(manager)
	manager.set_process(false)
	manager.npc_travel_requested.connect(_on_travel_requested)

	var resource_state := ResourceStateRecord.from_dictionary(
		{
			"schema_version": ResourceStateRecord.SCHEMA_VERSION,
			"node_id": "boundary_bush",
			"action_id": String(SimulationIds.ACTION_GATHER_FOOD),
			"resource_id": String(SimulationIds.RESOURCE_FOOD),
			"amount_remaining": 5.0,
			"yield_per_action": 1.0,
			"reserved_by": -1,
			"enabled": true,
			"can_npcs_use": true,
			"can_player_use": true
		}
	)
	manager.resource_states[resource_state.get_node_id()] = resource_state

	var npc: SimNPC = manager.npcs[0]
	npc.set_task(SimulationIds.ACTION_GATHER_FOOD)
	_check(
		manager.resolve_npc_target(npc.id, Vector3.ZERO),
		"Target resolver should resolve a resource from adapter facts"
	)
	_check(
		npc.target_id == &"boundary_bush" and resource_state.get_reserved_by() == npc.id,
		"Target resolver should authoritatively assign and reserve the target"
	)
	_check(
		travel_requests == [Vector3(3.0, 0.0, 2.0)],
		"Resolution should publish travel without moving a visual itself"
	)

	manager.release_npc_reservation(npc.id)
	npc.set_task(SimulationIds.ACTION_STUDY)
	_check(
		manager.resolve_npc_target(npc.id, Vector3.ZERO),
		"Target resolver should resolve an activity through the adapter"
	)
	_check(
		travel_requests.back() == adapter.activity_position,
		"Activity resolution should publish the adapter's position"
	)
	manager.free()
	adapter.free()


func _on_travel_requested(_npc: SimNPC, target_position: Vector3) -> void:
	travel_requests.append(target_position)


func _check(condition: bool, message: String) -> void:
	if not condition:
		failures.append(message)
