extends SceneTree

var failures: Array[String] = []


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


func _run() -> void:
	var manager: Node = load("res://simulation/SimulationManager.gd").new()
	manager.debug_logs = false
	root.add_child(manager)
	manager.set_process(false)

	var pantry: StorageStateRecord = manager.get_pantry()
	pantry.withdraw(SimulationIds.RESOURCE_FOOD, 1000.0)
	manager._sync_village_food()
	var resource := ResourceStateRecord.from_dictionary(
		{
			"schema_version": ResourceStateRecord.SCHEMA_VERSION,
			"node_id": "food_loop_bush",
			"action_id": String(SimulationIds.ACTION_GATHER_FOOD),
			"resource_id": String(SimulationIds.RESOURCE_FOOD),
			"amount_remaining": 10.0,
			"yield_per_action": 2.0,
			"reserved_by": -1,
			"enabled": true,
			"can_npcs_use": true,
			"can_player_use": true
		}
	)
	manager.resource_states[resource.get_node_id()] = resource

	var npc: SimNPC = manager.npcs[0]
	npc.hunger = 20.0
	npc.energy = 100.0
	npc.set_task(SimulationIds.ACTION_GATHER_FOOD, 1.0)
	npc.target_id = resource.get_node_id()
	resource.reserve(npc.id)
	npc.start_working()
	manager.simulate_tick()

	_check(
		(
			is_equal_approx(resource.get_amount_remaining(), 8.0)
			and is_equal_approx(npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 2.0)
		),
		"Gathering should move food from the source into NPC inventory"
	)
	_check(
		is_equal_approx(pantry.get_amount(SimulationIds.RESOURCE_FOOD), 0.0),
		"Gathering should not teleport food into storage"
	)

	manager.simulate_tick()
	_check(
		npc.current_task == SimulationIds.ACTION_DEPOSIT_FOOD,
		"Carried food should select the deposit action"
	)
	manager.notify_npc_arrived(npc.id)
	manager.simulate_tick()
	_check(
		(
			is_equal_approx(pantry.get_amount(SimulationIds.RESOURCE_FOOD), 2.0)
			and is_equal_approx(npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 0.0)
		),
		"Deposit should transfer carried food into the pantry"
	)

	npc.hunger = 80.0
	manager.simulate_tick()
	_check(
		npc.current_task == SimulationIds.ACTION_WITHDRAW_FOOD,
		"Hungry NPC should retrieve food from storage"
	)
	manager.notify_npc_arrived(npc.id)
	manager.simulate_tick()
	_check(
		(
			is_equal_approx(pantry.get_amount(SimulationIds.RESOURCE_FOOD), 1.0)
			and is_equal_approx(npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 1.0)
		),
		"Withdraw should transfer pantry food into NPC inventory"
	)

	manager.simulate_tick()
	_check(npc.current_task == SimulationIds.ACTION_EAT, "NPC carrying food should select eat")
	manager.notify_npc_arrived(npc.id)
	var hunger_before_eating := npc.hunger
	manager.simulate_tick()
	manager.simulate_tick()
	_check(
		(
			npc.hunger < hunger_before_eating
			and is_equal_approx(npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 0.0)
		),
		"Eating should consume carried food and reduce hunger"
	)

	var conserved_food := (
		resource.get_amount_remaining()
		+ pantry.get_amount(SimulationIds.RESOURCE_FOOD)
		+ npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD)
	)
	_check(
		is_equal_approx(conserved_food, 9.0),
		"Exactly one food unit should leave the world when eaten"
	)
	_check_economic_event_chain(manager)

	var restored: Node = load("res://simulation/SimulationManager.gd").new()
	restored.debug_logs = false
	root.add_child(restored)
	restored.set_process(false)
	_check(
		restored.restore_state_from_json(manager.serialize_state()),
		"Economic events should restore with the simulation"
	)
	_check(
		(
			restored.economic_events.size() == manager.economic_events.size()
			and restored.next_event_id == manager.next_event_id
		),
		"Economic event history and its next ID should round-trip"
	)

	if failures.is_empty():
		print("[TEST] Food storage loop passed")
		quit(0)
		return
	for failure in failures:
		push_error("[TEST] " + failure)
	quit(1)


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


func _check_economic_event_chain(manager: Node) -> void:
	var expected_types: Array[StringName] = [
		SimulationIds.EVENT_RESOURCE_EXTRACTED,
		SimulationIds.EVENT_STORAGE_DEPOSITED,
		SimulationIds.EVENT_STORAGE_WITHDRAWN,
		SimulationIds.EVENT_ITEM_CONSUMED
	]
	_check(
		manager.economic_events.size() == expected_types.size(),
		"Food transfer loop should emit one structured event per transfer"
	)
	if manager.economic_events.size() != expected_types.size():
		return
	for index in expected_types.size():
		var event: EconomicEventRecord = manager.economic_events[index]
		_check(
			(
				int(event.data["event_id"]) == index
				and StringName(event.data["event_type"]) == expected_types[index]
				and StringName(event.data["item_id"]) == SimulationIds.RESOURCE_FOOD
			),
			"Economic event order, identity, and item should be deterministic"
		)
	_check(
		(
			is_equal_approx(float(manager.economic_events[0].data["amount"]), 2.0)
			and is_equal_approx(float(manager.economic_events[1].data["amount"]), 2.0)
			and is_equal_approx(float(manager.economic_events[2].data["amount"]), 1.0)
			and is_equal_approx(float(manager.economic_events[3].data["amount"]), 1.0)
		),
		"Economic events should record exact transferred quantities"
	)
