extends SceneTree

## Synthetic ownership/lifetime/boundary/timing regression for the prepared
## static M2 Mesh resource cache.

const CACHE_SCRIPT := preload("res://src/render/m2/m2_mesh_resource_cache_state.gd")
const CACHE_PATH := "res://src/render/m2/m2_mesh_resource_cache_state.gd"
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
const STATIC_OBSERVER_PATH := "res://src/render/m2/m2_static_build_resource_observer.gd"
const RESOURCE_FINALIZER_PATH := "res://src/render/m2/m2_mesh_resource_finalizer.gd"


func _initialize() -> void:
	var failures: Array[String] = []
	_verify_validation_store_lookup_and_replace(failures)
	_verify_clear_and_diagnostics(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_CACHE_STATE: %s" % failure)
		quit(1)
		return
	print(
		"M2_MESH_RESOURCE_CACHE_STATE PASS cases=9 iterations=100 elapsed_ms=%.3f"
		% elapsed_milliseconds
	)
	quit(0)


func _verify_validation_store_lookup_and_replace(failures: Array[String]) -> void:
	var cache: RefCounted = CACHE_SCRIPT.new()
	var first_mesh := ArrayMesh.new()
	var replacement_mesh := ArrayMesh.new()
	_expect_false(cache.call("store_mesh", "", first_mesh), "empty path rejected", failures)
	_expect_false(cache.call("store_mesh", "world/a.m2", null), "null Mesh rejected", failures)
	_expect_true(cache.call("store_mesh", "world/a.m2", first_mesh), "first Mesh stored", failures)
	_expect_true(cache.call("has_mesh", "world/a.m2"), "stored Mesh observable", failures)
	_expect_same(cache.call("find_mesh", "world/a.m2"), first_mesh, "exact Mesh retained", failures)
	_expect_true(
		cache.call("store_mesh", "world/a.m2", replacement_mesh),
		"same-path Mesh replaced",
		failures
	)
	_expect_same(
		cache.call("find_mesh", "world/a.m2"),
		replacement_mesh,
		"replacement Mesh retained",
		failures
	)
	_expect_equal(int(cache.call("mesh_count")), 1, "replacement keeps one entry", failures)
	_expect_same(cache.call("find_mesh", "world/missing.m2"), null, "unknown path returns null", failures)


func _verify_clear_and_diagnostics(failures: Array[String]) -> void:
	var cache: RefCounted = CACHE_SCRIPT.new()
	cache.call("store_mesh", "world/a.m2", ArrayMesh.new())
	cache.call("store_mesh", "world/b.m2", ArrayMesh.new())
	var normalized_paths: Array[String] = cache.call("normalized_paths_snapshot")
	_expect_equal(normalized_paths.size(), 2, "diagnostic path count", failures)
	_expect_string_equal(normalized_paths[0], "world/a.m2", "diagnostic insertion order first", failures)
	_expect_string_equal(normalized_paths[1], "world/b.m2", "diagnostic insertion order second", failures)
	normalized_paths.clear()
	_expect_equal(int(cache.call("mesh_count")), 2, "diagnostics detached", failures)
	cache.call("clear")
	cache.call("clear")
	_expect_equal(int(cache.call("mesh_count")), 0, "clear releases all entries", failures)


func _verify_ownership_boundaries(failures: Array[String]) -> void:
	var cache_source := FileAccess.get_file_as_string(CACHE_PATH)
	var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
	var observer_source := FileAccess.get_file_as_string(STATIC_OBSERVER_PATH)
	var resource_finalizer_source := FileAccess.get_file_as_string(
		RESOURCE_FINALIZER_PATH
	)
	_expect_true(
		loader_source.contains("M2_MESH_RESOURCE_CACHE_STATE_SCRIPT.new()"),
		"loader composes Mesh cache state",
		failures
	)
	_expect_false(loader_source.contains("var _m2_mesh_cache:"), "legacy Mesh cache removed", failures)
	_expect_equal(
		loader_source.count("_m2_mesh_resource_cache_state.store_mesh(")
		+ resource_finalizer_source.count("\"store_mesh\""),
		2,
		"two existing stores delegate",
		failures
	)
	_expect_equal(
		loader_source.count("_m2_mesh_resource_cache_state.find_mesh(")
		+ observer_source.count("\"find_mesh\""),
		2,
		"two existing lookups delegate",
		failures
	)
	_expect_equal(
		loader_source.count("_m2_mesh_resource_cache_state.clear()"),
		1,
		"final shutdown clear delegates",
		failures
	)
	for retained_renderer_rule in [
		"ResourceLoader.load_threaded_get(resource_path)",
		"\"extract_first_mesh\"",
		"prepare_mesh_for_runtime(",
		"\"mark_model_missing\"",
		"_m2_prototype_cache_state.adopt_static_prototype(",
	]:
		_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.",
		"PackedScene",
		"Node3D",
		"M2Builder",
		"_m2_prototype_cache_state",
	]:
		_expect_false(
			cache_source.contains(forbidden_dependency),
			"cache omits %s ownership" % forbidden_dependency,
			failures
		)


func _verify_bounded_timing(failures: Array[String]) -> float:
	var cache: RefCounted = CACHE_SCRIPT.new()
	var meshes: Array[Mesh] = []
	for _path_index in range(256):
		meshes.append(ArrayMesh.new())
	var started_microseconds := Time.get_ticks_usec()
	for _iteration in range(100):
		for path_index in range(256):
			cache.call("store_mesh", "world/model_%d.m2" % path_index, meshes[path_index])
		for path_index in range(256):
			cache.call("find_mesh", "world/model_%d.m2" % path_index)
		cache.call("clear")
	var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
	_expect_true(elapsed_milliseconds < 1000.0, "100 by 256 store/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_string_equal(actual: String, expected: String, label: String, failures: Array[String]) -> void:
	if actual != expected:
		failures.append("%s expected=%s actual=%s" % [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)
