extends SceneTree

const OBSERVER_SCRIPT := preload("res://src/render/m2/m2_static_build_resource_observer.gd")
const SNAPSHOT_SCRIPT := preload("res://src/render/m2/m2_build_resource_snapshot.gd")
const MESH_CACHE_SCRIPT := preload("res://src/render/m2/m2_mesh_resource_cache_state.gd")
const PROTOTYPE_CACHE_SCRIPT := preload("res://src/render/m2/m2_prototype_cache_state.gd")
const PIPELINE_SCRIPT := preload("res://src/render/m2/m2_mesh_load_pipeline_state.gd")
const OBSERVER_PATH := "res://src/render/m2/m2_static_build_resource_observer.gd"
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"


func _initialize() -> void:
	var failures: Array[String] = []
	_verify_invalid_cached_missing_pending(failures)
	_verify_request_and_path_order(failures)
	_verify_source_boundaries(failures)
	var elapsed := _verify_timing(failures)
	if not failures.is_empty():
		for failure in failures:
			push_error("M2_STATIC_BUILD_RESOURCE_OBSERVER: %s" % failure)
		quit(1)
		return
	print("M2_STATIC_BUILD_RESOURCE_OBSERVER PASS cases=11 iterations=20000 elapsed_ms=%.3f" % elapsed)
	quit(0)


func _verify_invalid_cached_missing_pending(failures: Array[String]) -> void:
	var observer: RefCounted = OBSERVER_SCRIPT.new()
	var mesh_cache: RefCounted = MESH_CACHE_SCRIPT.new()
	var prototype_cache: RefCounted = PROTOTYPE_CACHE_SCRIPT.new()
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	var snapshot: RefCounted = SNAPSHOT_SCRIPT.new("world/a.m2", null, false)
	_expect(
		observer.call(
			"observe", null, "world/a.m2", "res://none",
			mesh_cache, prototype_cache, pipeline
		)
		== OBSERVER_SCRIPT.OUTCOME_REJECTED,
		"null snapshot rejected", failures
	)
	var mesh := ArrayMesh.new()
	mesh_cache.call("store_mesh", "world/a.m2", mesh)
	_expect(
		observer.call(
			"observe", snapshot, "world/a.m2", "res://none",
			mesh_cache, prototype_cache, pipeline
		)
		== OBSERVER_SCRIPT.OUTCOME_CACHED, "cached outcome", failures
	)
	_expect(is_same(snapshot.call("static_mesh"), mesh), "cached Mesh identity", failures)
	mesh_cache.call("clear")
	prototype_cache.call("mark_model_missing", "world/a.m2")
	_expect(
		observer.call(
			"observe", snapshot, "world/a.m2", "res://none",
			mesh_cache, prototype_cache, pipeline
		)
		== OBSERVER_SCRIPT.OUTCOME_MISSING, "missing outcome", failures
	)
	_expect(bool(snapshot.call("static_model_missing")), "missing adopted", failures)
	prototype_cache = PROTOTYPE_CACHE_SCRIPT.new()
	pipeline.call("remember_request", "world/a.m2", "res://pending.tscn")
	_expect(
		observer.call(
			"observe", snapshot, "world/a.m2", "res://none",
			mesh_cache, prototype_cache, pipeline
		)
		== OBSERVER_SCRIPT.OUTCOME_PENDING, "pending outcome", failures
	)
	_expect(not bool(snapshot.call("static_model_missing")), "pending is not missing", failures)
	pipeline.call("clear")
	prototype_cache.call("clear_and_release")


func _verify_request_and_path_order(failures: Array[String]) -> void:
	var observer: RefCounted = OBSERVER_SCRIPT.new()
	var extensions: Array[String] = [".tscn", ".glb"]
	var paths: PackedStringArray = observer.call(
		"cache_resource_paths", "res://cache", "World/Foo/Bar.M2", extensions
	)
	var expected := PackedStringArray([
		"res://cache/World/Foo/Bar.tscn", "res://cache/world/foo/bar.tscn",
		"res://cache/Bar.tscn", "res://cache/bar.tscn",
		"res://cache/World/Foo/Bar.glb", "res://cache/world/foo/bar.glb",
		"res://cache/Bar.glb", "res://cache/bar.glb",
	])
	_expect(paths == expected, "candidate order", failures)
	_expect(
		observer.call("glb_animation_schema", "res://missing.glb") == "",
		"missing GLB schema empty", failures
	)


func _verify_source_boundaries(failures: Array[String]) -> void:
	var loader := FileAccess.get_file_as_string(LOADER_PATH)
	var observer := FileAccess.get_file_as_string(OBSERVER_PATH)
	_expect(
		loader.contains("_m2_static_build_resource_observer.observe("),
		"loader delegates observer", failures
	)
	_expect(not loader.contains("func _get_m2_mesh_or_request"), "legacy lookup removed", failures)
	_expect(not loader.contains("func _request_m2_mesh_load"), "legacy request removed", failures)
	_expect(
		observer.contains("ResourceLoader.load_threaded_request("),
		"observer owns request", failures
	)
	for forbidden in ["queue_free", "RenderingServer", "WorkerThreadPool", "Mutex"]:
		_expect(not observer.contains(forbidden), "observer omits %s" % forbidden, failures)


func _verify_timing(failures: Array[String]) -> float:
	var observer: RefCounted = OBSERVER_SCRIPT.new()
	var extensions: Array[String] = [".tscn", ".glb"]
	var started := Time.get_ticks_usec()
	for index in range(20000):
		observer.call(
			"cache_resource_paths", "res://cache",
			"world/%d/model.m2" % (index % 64), extensions
		)
	var elapsed := float(Time.get_ticks_usec() - started) / 1000.0
	_expect(elapsed < 1000.0, "path planning bounded", failures)
	return elapsed


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