extends SceneTree

## Synthetic direct-Resource/PackedScene/subtree/order/lifetime/boundary/timing
## regression for first-Mesh extraction.

const EXTRACTOR_SCRIPT := preload("res://src/render/m2/m2_mesh_resource_extractor.gd")
const EXTRACTOR_PATH := "res://src/render/m2/m2_mesh_resource_extractor.gd"
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
const RESOURCE_FINALIZER_PATH := "res://src/render/m2/m2_mesh_resource_finalizer.gd"


func _initialize() -> void:
	var failures: Array[String] = []
	_verify_direct_and_invalid_resources(failures)
	_verify_depth_first_subtree_order(failures)
	_verify_packed_scene_mesh_and_temporary_lifetime(failures)
	_verify_ownership_boundaries(failures)
	var elapsed_milliseconds := _verify_bounded_timing(failures)
	if not failures.is_empty():
		for failure in failures:
			push_error("M2_MESH_RESOURCE_EXTRACTOR: %s" % failure)
		quit(1)
		return
	print(
		"M2_MESH_RESOURCE_EXTRACTOR PASS cases=10 iterations=10000 elapsed_ms=%.3f"
		% elapsed_milliseconds
	)
	quit(0)


func _verify_direct_and_invalid_resources(failures: Array[String]) -> void:
	var extractor: RefCounted = EXTRACTOR_SCRIPT.new()
	var direct_mesh := ArrayMesh.new()
	_expect_same(
		extractor.call("extract_first_mesh", direct_mesh),
		direct_mesh,
		"direct Mesh reference retained",
		failures
	)
	_expect_same(extractor.call("extract_first_mesh", Resource.new()), null, "unsupported Resource returns null", failures)
	_expect_same(extractor.call("extract_first_mesh", null), null, "null Resource returns null", failures)
	_expect_same(extractor.call("find_first_mesh_in_subtree", null), null, "null subtree returns null", failures)


func _verify_depth_first_subtree_order(failures: Array[String]) -> void:
	var extractor: RefCounted = EXTRACTOR_SCRIPT.new()
	var root := Node3D.new()
	var first_branch := Node3D.new()
	var second_branch := Node3D.new()
	var first_mesh_instance := MeshInstance3D.new()
	var second_mesh_instance := MeshInstance3D.new()
	var first_mesh := ArrayMesh.new()
	var second_mesh := ArrayMesh.new()
	first_mesh_instance.mesh = first_mesh
	second_mesh_instance.mesh = second_mesh
	root.add_child(first_branch)
	root.add_child(second_branch)
	first_branch.add_child(first_mesh_instance)
	second_branch.add_child(second_mesh_instance)
	_expect_same(
		extractor.call("find_first_mesh_in_subtree", root),
		first_mesh,
		"depth-first child order retained",
		failures
	)
	_expect_same(first_mesh_instance.get_parent(), first_branch, "subtree ownership unchanged", failures)
	first_mesh_instance.mesh = null
	_expect_same(
		extractor.call("find_first_mesh_in_subtree", root),
		second_mesh,
		"null MeshInstance Mesh skipped by parent traversal",
		failures
	)
	root.free()


func _verify_packed_scene_mesh_and_temporary_lifetime(failures: Array[String]) -> void:
	var extractor: RefCounted = EXTRACTOR_SCRIPT.new()
	var source_root := Node3D.new()
	var mesh_instance := MeshInstance3D.new()
	var packed_mesh := ArrayMesh.new()
	mesh_instance.mesh = packed_mesh
	source_root.add_child(mesh_instance)
	mesh_instance.owner = source_root
	var packed_scene := PackedScene.new()
	_expect_equal(packed_scene.pack(source_root), OK, "synthetic PackedScene packs", failures)
	source_root.free()
	var node_count_before := int(Performance.get_monitor(Performance.OBJECT_NODE_COUNT))
	var extracted_mesh: Mesh = extractor.call("extract_first_mesh", packed_scene)
	var node_count_after := int(Performance.get_monitor(Performance.OBJECT_NODE_COUNT))
	_expect_same(extracted_mesh, packed_mesh, "PackedScene first Mesh retained", failures)
	_expect_equal(node_count_after, node_count_before, "temporary PackedScene root freed", failures)


func _verify_ownership_boundaries(failures: Array[String]) -> void:
	var extractor_source := FileAccess.get_file_as_string(EXTRACTOR_PATH)
	var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
	var resource_finalizer_source := FileAccess.get_file_as_string(
		RESOURCE_FINALIZER_PATH
	)
	_expect_true(
		loader_source.contains("M2_MESH_RESOURCE_EXTRACTOR_SCRIPT.new()"),
		"loader composes Mesh resource extractor",
		failures
	)
	_expect_false(
		loader_source.contains("func _extract_first_mesh_from_m2_resource("),
		"legacy Resource extractor removed",
		failures
	)
	_expect_false(
		loader_source.contains("func _find_first_mesh_recursive("),
		"legacy subtree extractor removed",
		failures
	)
	_expect_equal(
		resource_finalizer_source.count("\"extract_first_mesh\""),
		1,
		"threaded Resource finalization delegates",
		failures
	)
	_expect_equal(
		loader_source.count("_m2_mesh_resource_extractor.find_first_mesh_in_subtree("),
		1,
		"synchronous fallback delegates",
		failures
	)
	for retained_renderer_rule in [
		"ResourceLoader.load_threaded_get(resource_path)",
		"prepare_mesh_for_runtime(",
		"\"store_mesh\"",
		"M2_BUILDER_SCRIPT.build(data, extracted_dir)",
		"\"mark_model_missing\"",
	]:
		_expect_true(
			(loader_source + resource_finalizer_source).contains(retained_renderer_rule),
			"renderer retains %s" % retained_renderer_rule,
			failures
		)
	for forbidden_dependency in [
		"ResourceLoader.",
		"FileAccess.",
		"WorkerThreadPool.",
		"M2Builder",
		"_m2_mesh_resource_cache_state",
		"_m2_prototype_cache_state",
		"MultiMesh",
	]:
		_expect_false(
			extractor_source.contains(forbidden_dependency),
			"extractor omits %s ownership" % forbidden_dependency,
			failures
		)
	_expect_true(extractor_source.contains("temporary_root.free()"), "temporary root free remains explicit", failures)


func _verify_bounded_timing(failures: Array[String]) -> float:
	var extractor: RefCounted = EXTRACTOR_SCRIPT.new()
	var root := Node3D.new()
	var branch := root
	for _depth_index in range(8):
		var child_branch := Node3D.new()
		branch.add_child(child_branch)
		branch = child_branch
	var mesh_instance := MeshInstance3D.new()
	mesh_instance.mesh = ArrayMesh.new()
	branch.add_child(mesh_instance)
	var started_microseconds := Time.get_ticks_usec()
	for _iteration in range(10000):
		extractor.call("find_first_mesh_in_subtree", root)
	var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
	root.free()
	_expect_true(elapsed_milliseconds < 1000.0, "10000 subtree lookups under 1 second", failures)
	return elapsed_milliseconds


func _expect_true(condition: bool, label: String, failures: Array[String]) -> void:
	if not condition:
		failures.append(label)


func _expect_false(condition: bool, label: String, failures: Array[String]) -> void:
	_expect_true(not condition, label, failures)


func _expect_equal(actual: int, expected: int, label: String, failures: Array[String]) -> void:
	if actual != expected:
		failures.append("%s expected=%d actual=%d" % [label, expected, actual])


func _expect_same(actual: Variant, expected: Variant, label: String, failures: Array[String]) -> void:
	if not is_same(actual, expected):
		failures.append(label)
