extends SceneTree

var failures: Array[String] = []


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


func _run() -> void:
	var camera := Camera3D.new()
	camera.current = true
	root.add_child(camera)

	var world: Node3D = load("res://world/jajce/JajceWorld.tscn").instantiate()
	root.add_child(world)
	await process_frame
	for frame in 10:
		await physics_frame

	var terrain = world.get_node("TerrainRoot/Terrain3D")
	_check(terrain is Terrain3D, "JajceWorld should own a Terrain3D node")
	_check(
		terrain.data_directory == "res://terrain/jajce/data",
		"Terrain3D should use dedicated Jajce data"
	)
	_check(
		terrain.assets.get_texture_count() >= 3,
		(
			"Jajce terrain should expose at least three materials (found %s)"
			% terrain.assets.get_texture_count()
		)
	)
	_check(
		absf(terrain.data.get_height(Vector3.ZERO)) < 0.1,
		"Central gameplay area should remain level for current navigation"
	)
	_check(
		terrain.data.get_height(Vector3(-72, 0, 18)) > 10.0,
		"Authored terrain should raise the western fortress ridge"
	)
	_check(world.has_node("WaterRoot"), "JajceWorld should expose WaterRoot")
	_check(world.has_node("VillageRoot"), "JajceWorld should expose VillageRoot")
	_check(world.has_node("FoliageRoot"), "JajceWorld should expose FoliageRoot")
	_check(
		world.get_node("FoliageRoot").get_child_count() >= 10,
		"JajceWorld should include restrained authored foliage clusters"
	)
	_check(world.has_node("FortressBlockout"), "JajceWorld should include the fortress landmark")
	_check(world.has_node("WaterRoot/WaterfallBody"), "WaterRoot should include a waterfall drop")
	_check(
		world.has_node("WaterRoot/WaterfallMist"),
		"WaterRoot should include waterfall mist particles"
	)
	_check(world.has_node("WaterRoot/RiverSurface"), "WaterRoot should include a river surface")
	var environment: Environment = (
		(world.get_node("WorldEnvironment") as WorldEnvironment).environment
	)
	_check(
		environment.fog_enabled and environment.sky != null,
		"Jajce environment should provide sky and restrained valley fog"
	)
	_check(
		world.has_node("VillageRoot/Mill") and world.has_node("VillageRoot/Bridge"),
		"VillageRoot should include the mill and bridge blockouts"
	)
	var smoke_count := 0
	for suffix in ["House_01", "House_02", "House_03"]:
		if world.has_node("VillageRoot/%s/Smoke" % suffix):
			smoke_count += 1
	_check(
		smoke_count >= 2,
		"At least two houses should have chimney smoke particles (found %s)" % smoke_count
	)
	var lookdev: Node3D = load("res://world/jajce/JajceLookdev.tscn").instantiate()
	_check(lookdev.has_node("JajceWorld"), "Lookdev should instance JajceWorld")
	_check(lookdev.has_node("BeautyCamera"), "Lookdev should provide a beauty camera")
	lookdev.free()

	var resources := world.get_node("WorldObjects/ResourceNodes").get_children()
	var ids: Array[String] = []
	for resource in resources:
		ids.append(String((resource as ResourceNode).node_id))
	ids.sort()
	_check(
		ids == ["berry_bush_01", "berry_bush_02", "tree_01", "tree_02"],
		"Jajce resources should preserve all stable IDs"
	)

	var navigation_region := world.get_node("NavigationRegion3D") as NavigationRegion3D
	var navigation_map: RID = NavigationServer3D.region_get_map(navigation_region.get_rid())
	_check(navigation_map.is_valid(), "Jajce navigation region should be assigned to a map")
	_check(
		navigation_region.navigation_mesh.get_polygon_count() > 0,
		"Jajce navigation mesh should contain baked polygons"
	)
	await _wait_for_navigation_map(navigation_map)
	NavigationServer3D.map_force_update(navigation_map)

	var origins := [Vector3(-6, 0, -4), Vector3(0, 0, 0), Vector3(6, 0, 4)]
	var destinations: Array[Vector3] = []
	for resource in resources:
		destinations.append((resource as ResourceNode).interaction_point.global_position)
	for marker in world.get_node("LegacyActivityMarkers").get_children():
		destinations.append((marker as Marker3D).global_position)

	for origin in origins:
		for destination in destinations:
			var path := NavigationServer3D.map_get_path(navigation_map, origin, destination, true)
			_check(
				not path.is_empty(),
				"Jajce navigation path should exist from %s to %s" % [origin, destination]
			)

	var adapter := ActiveWorldAdapter.new()
	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)
	await process_frame
	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 find Jajce resources without a parent path"
	)
	var selected := ResourceNode.get_by_id(npc.target_id)
	if selected != null:
		var selected_state: ResourceStateRecord = manager.get_resource_state(selected.node_id)
		_check(
			selected_state.get_reserved_by() == npc.id,
			"Selected Jajce resource should be authoritatively reserved"
		)
		manager.release_resource(selected.node_id, npc.id)

	if failures.is_empty():
		print(
			"[TEST] Jajce scaffold passed: Terrain3D, 6 textures, shader water, building blockouts, chimney smoke"
		)
		quit(0)
		return

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


func _wait_for_navigation_map(navigation_map: RID) -> void:
	for attempt in 30:
		if (
			NavigationServer3D.map_get_iteration_id(navigation_map) > 0
			and not NavigationServer3D.map_get_regions(navigation_map).is_empty()
		):
			await physics_frame
			return
		await physics_frame
	_check(false, "Jajce navigation map did not synchronize within 30 physics frames")


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