extends SceneTree

## Asset-free formula, dependency and bounded timing regression for chunk LOD.

const PLANNER_SCRIPT := preload("res://src/render/terrain/terrain_chunk_lod_planner.gd")
const POLICY_SCRIPT := preload("res://src/render/terrain/terrain_chunk_lod_policy.gd")
const POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"


func _initialize() -> void:
	var failures: Array[String] = []
	_verify_formula(failures)
	_verify_disabled_and_custom_policy(failures)
	_verify_loader_boundary(failures)
	var elapsed_milliseconds := _verify_bounded_timing(failures)
	if not failures.is_empty():
		for failure in failures:
			push_error("TERRAIN_CHUNK_LOD_PLANNER: %s" % failure)
		quit(1)
		return
	print("TERRAIN_CHUNK_LOD_PLANNER PASS cases=7 iterations=250 elapsed_ms=%.3f" % elapsed_milliseconds)
	quit(0)


func _verify_formula(failures: Array[String]) -> void:
	var planner: RefCounted = PLANNER_SCRIPT.new()
	var policy: RefCounted = POLICY_SCRIPT.new(true, 1, 2, 3, 10.0)
	var focus: RefCounted = POSITION_SCRIPT.new(0.0, 9999.0, 0.0)
	var chunks: Array = [
		{"origin": Vector3(-5.0, 2000.0, -5.0)},
		{"origin": Vector3(5.0, -3000.0, -5.0)},
		{"origin": Vector3(5.001, 0.0, -5.0)},
		{"origin": Vector3(15.0, 0.0, -5.0)},
		{"origin": Vector3(20.0, 0.0, -5.0)},
		{"origin": Vector3(25.0, 0.0, -5.0)},
		{"origin": Vector3(25.001, 0.0, -5.0)},
		{},
	]
	var desired: Dictionary = planner.call("plan", chunks, focus, policy)
	_expect_equal(int(desired.get(0, -1)), 0, "center ignores vertical distance", failures)
	_expect_equal(int(desired.get(1, -1)), 0, "lod0 inclusive boundary", failures)
	_expect_equal(int(desired.get(2, -1)), 1, "just outside lod0", failures)
	_expect_equal(int(desired.get(3, -1)), 1, "lod1 inclusive boundary", failures)
	_expect_equal(int(desired.get(4, -1)), 2, "between lod1 and lod2", failures)
	_expect_equal(int(desired.get(5, -1)), 2, "lod2 inclusive boundary", failures)
	_expect_true(not desired.has(6) and not desired.has(7), "outside and sparse chunks absent", failures)


func _verify_disabled_and_custom_policy(failures: Array[String]) -> void:
	var planner: RefCounted = PLANNER_SCRIPT.new()
	var focus: RefCounted = POSITION_SCRIPT.new(0.0, 0.0, 0.0)
	var chunks: Array = [{"origin": Vector3(-5.0, 0.0, -5.0)}]
	_expect_true((planner.call("plan", chunks, focus, POLICY_SCRIPT.new(false, 1, 2, 3, 10.0)) as Dictionary).is_empty(), "disabled policy empty", failures)
	var negative_policy: RefCounted = POLICY_SCRIPT.new(true, -1, -2, -3, 10.0)
	_expect_equal(int((planner.call("plan", chunks, focus, negative_policy) as Dictionary).get(0, -1)), 0, "negative radius keeps squared semantics", failures)


func _verify_loader_boundary(failures: Array[String]) -> void:
	var source := _read_text(LOADER_PATH, failures)
	_expect_true(source.contains("TERRAIN_CHUNK_LOD_PLANNER_SCRIPT.new()"), "loader composes planner", failures)
	_expect_true(source.contains("_terrain_chunk_lod_planner.plan(chunks, typed_focus_position, terrain_chunk_lod_policy)"), "loader delegates plan", failures)
	_expect_true(not source.contains("dist_sq <= lod0_sq"), "loader omits chunk LOD formula", failures)


func _verify_bounded_timing(failures: Array[String]) -> float:
	var planner: RefCounted = PLANNER_SCRIPT.new()
	var policy: RefCounted = POLICY_SCRIPT.new(true, 10, 20, 40, 33.333333)
	var focus: RefCounted = POSITION_SCRIPT.new(1000.0, 0.0, 1000.0)
	var chunks: Array = []
	for chunk_index in range(256):
		chunks.append({"origin": Vector3(float(chunk_index % 16) * 33.333333, 0.0, float(chunk_index / 16) * 33.333333)})
	var started_microseconds := Time.get_ticks_usec()
	for _iteration in range(250):
		planner.call("plan", chunks, focus, policy)
	var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
	_expect_true(elapsed_milliseconds < 1000.0, "250 plans remain bounded", failures)
	return elapsed_milliseconds


func _read_text(path: String, failures: Array[String]) -> String:
	var file := FileAccess.open(path, FileAccess.READ)
	if file == null:
		failures.append("cannot open %s" % path)
		return ""
	return file.get_as_text()


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


func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
	if not actual_value:
		failures.append("%s expected true" % label)
