extends SceneTree

## Asset-free lifecycle, threading, boundary and timing regression for ADT water loads.

const PIPELINE_SCRIPT := preload(
	"res://src/render/liquid/adt_water_load_pipeline_state.gd"
)
const PIPELINE_PATH := "res://src/render/liquid/adt_water_load_pipeline_state.gd"
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"


func _initialize() -> void:
	var failures: Array[String] = []
	_verify_invalid_dedupe_and_fifo(failures)
	_verify_active_task_lifecycle(failures)
	_verify_result_mailbox_and_payload_identity(failures)
	_verify_worker_publication(failures)
	_verify_pending_cancellation(failures)
	_verify_clear_and_detached_diagnostics(failures)
	_verify_ownership_boundaries(failures)
	var elapsed_milliseconds := _verify_bounded_timing(failures)
	if not failures.is_empty():
		for failure in failures:
			push_error("ADT_WATER_LOAD_PIPELINE_STATE: %s" % failure)
		quit(1)
		return
	print(
		"ADT_WATER_LOAD_PIPELINE_STATE PASS cases=12 iterations=100 elapsed_ms=%.3f"
		% elapsed_milliseconds
	)
	quit(0)


func _verify_invalid_dedupe_and_fifo(failures: Array[String]) -> void:
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	_expect_false(
		pipeline.call("enqueue_request", "", "res://tile.adt"),
		"empty tile key rejected",
		failures
	)
	_expect_false(
		pipeline.call("enqueue_request", "31:49", ""),
		"empty path rejected",
		failures
	)
	_expect_true(
		pipeline.call("enqueue_request", "31:49", "res://a.adt"),
		"first request accepted",
		failures
	)
	_expect_false(
		pipeline.call("enqueue_request", "31:49", "res://duplicate.adt"),
		"queued duplicate rejected",
		failures
	)
	pipeline.call("enqueue_request", "32:49", "res://b.adt")
	_expect_equal(int(pipeline.call("pending_request_count")), 2, "pending count", failures)
	var first_request: Dictionary = pipeline.call("take_next_request")
	var second_request: Dictionary = pipeline.call("take_next_request")
	_expect_string_equal(String(first_request["key"]), "31:49", "FIFO first key", failures)
	_expect_string_equal(String(second_request["key"]), "32:49", "FIFO second key", failures)
	_expect_true(
		pipeline.call("enqueue_request", "31:49", "res://retry.adt"),
		"dequeued key can be requested again",
		failures
	)


func _verify_active_task_lifecycle(failures: Array[String]) -> void:
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	_expect_false(
		pipeline.call("remember_active_task", "", 7),
		"empty active key rejected",
		failures
	)
	_expect_true(
		pipeline.call("remember_active_task", "31:49", 101),
		"active task accepted",
		failures
	)
	_expect_false(
		pipeline.call("remember_active_task", "31:49", 202),
		"duplicate active task rejected",
		failures
	)
	_expect_true(
		pipeline.call("has_active_task", "31:49"),
		"active task observable",
		failures
	)
	_expect_equal(
		int(pipeline.call("active_task_id_for", "31:49")),
		101,
		"opaque task ID retained",
		failures
	)
	_expect_false(
		pipeline.call("enqueue_request", "31:49", "res://active.adt"),
		"active tile rejects enqueue",
		failures
	)
	_expect_equal(
		int(pipeline.call("complete_active_task", "31:49")),
		101,
		"completion returns task ID",
		failures
	)
	_expect_equal(
		int(pipeline.call("complete_active_task", "31:49")),
		-1,
		"unknown completion rejected",
		failures
	)


func _verify_result_mailbox_and_payload_identity(failures: Array[String]) -> void:
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	var water_data := {"chunks": [{"liquid": true}]}
	_expect_false(
		pipeline.call("publish_result", "", "res://a.adt", water_data),
		"empty result key rejected",
		failures
	)
	_expect_true(
		pipeline.call("publish_result", "31:49", "res://a.adt", water_data),
		"valid result published",
		failures
	)
	_expect_true(pipeline.call("has_result"), "published result observable", failures)
	var result: Dictionary = pipeline.call("pop_result")
	_expect_string_equal(String(result["key"]), "31:49", "result key", failures)
	_expect_string_equal(String(result["path"]), "res://a.adt", "result path", failures)
	_expect_true(result["data"] == water_data, "water payload identity retained", failures)
	_expect_false(pipeline.call("has_result"), "result pop empties mailbox", failures)
	_expect_true(
		(pipeline.call("pop_result") as Dictionary).is_empty(),
		"empty result pop is safe",
		failures
	)


func _verify_worker_publication(failures: Array[String]) -> void:
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	var worker := Thread.new()
	var start_error := worker.start(
		Callable(pipeline, "publish_result").bind(
			"worker",
			"res://worker.adt",
			{"worker": true}
		)
	)
	_expect_equal(int(start_error), OK, "worker publication starts", failures)
	if start_error == OK:
		worker.wait_to_finish()
	_expect_true(pipeline.call("has_result"), "worker result safely visible", failures)
	var result: Dictionary = pipeline.call("pop_result")
	_expect_string_equal(String(result.get("key", "")), "worker", "worker result key", failures)


func _verify_pending_cancellation(failures: Array[String]) -> void:
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	pipeline.call("enqueue_request", "31:49", "res://a.adt")
	pipeline.call("enqueue_request", "32:49", "res://b.adt")
	pipeline.call("remember_active_task", "31:49-active", 12)
	_expect_equal(
		int(pipeline.call("cancel_pending_requests", "31:49")),
		1,
		"tile cancellation removes pending request",
		failures
	)
	_expect_equal(int(pipeline.call("pending_request_count")), 1, "other pending retained", failures)
	_expect_true(
		pipeline.call("has_active_task", "31:49-active"),
		"pending cancellation leaves active work",
		failures
	)
	_expect_equal(
		int(pipeline.call("cancel_pending_requests", "unknown")),
		0,
		"unknown cancellation unchanged",
		failures
	)


func _verify_clear_and_detached_diagnostics(failures: Array[String]) -> void:
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	pipeline.call("enqueue_request", "32:49", "res://b.adt")
	pipeline.call("enqueue_request", "31:49", "res://a.adt")
	pipeline.call("remember_active_task", "z", 2)
	pipeline.call("remember_active_task", "a", 1)
	pipeline.call("publish_result", "result", "res://result.adt", {"secret": "payload"})
	var snapshot: Dictionary = pipeline.call("diagnostic_snapshot")
	_expect_string_array(
		snapshot["active_tile_keys"],
		["a", "z"],
		"active diagnostics sorted",
		failures
	)
	_expect_equal(
		(snapshot["pending_requests"] as Array).size(),
		2,
		"pending diagnostics count",
		failures
	)
	_expect_false(
		(snapshot["result_records"][0] as Dictionary).has("data"),
		"diagnostics omit water payload",
		failures
	)
	(snapshot["pending_requests"] as Array).clear()
	var fresh_snapshot: Dictionary = pipeline.call("diagnostic_snapshot")
	_expect_equal(
		(fresh_snapshot["pending_requests"] as Array).size(),
		2,
		"diagnostics detached",
		failures
	)
	pipeline.call("clear")
	pipeline.call("clear")
	_expect_equal(int(pipeline.call("total_load_count")), 0, "clear load count", failures)
	_expect_false(pipeline.call("has_result"), "clear result mailbox", failures)


func _verify_ownership_boundaries(failures: Array[String]) -> void:
	var loader_source := _read_text(LOADER_PATH, failures)
	var pipeline_source := _read_text(PIPELINE_PATH, failures)
	_expect_true(
		loader_source.contains("ADT_WATER_LOAD_PIPELINE_STATE_SCRIPT.new()"),
		"loader composes pipeline state",
		failures
	)
	for legacy_field in [
		"var _water_load_queue:",
		"var _water_load_queued:",
		"var _water_load_tasks:",
		"var _water_result_mutex",
		"var _water_result_queue:",
	]:
		_expect_false(loader_source.contains(legacy_field), "loader removes %s" % legacy_field, failures)
	for retained_loader_rule in [
		"ClassDB.instantiate(\"ADTLoader\")",
		"WorkerThreadPool.add_task(_load_tile_water_task.bind(key, path))",
		"RENDER_BUDGET_SCHEDULER_SCRIPT.WATER_FINALIZE",
		"_adt_water_scene_finalizer.attach_water_scene(",
		"state[\"water_loaded\"] = true",
	]:
		_expect_true(loader_source.contains(retained_loader_rule), "loader retains %s" % retained_loader_rule, failures)
	for forbidden_token in [
		"WorkerThreadPool.",
		"ADTLoader",
		"RenderBudgetScheduler",
		"Node3D",
		"Mesh",
		"RID",
		"_tile_states",
	]:
		_expect_false(
			pipeline_source.contains(forbidden_token),
			"pipeline omits %s ownership" % forbidden_token,
			failures
		)
	_expect_equal(
		_count_occurrences(loader_source, "_adt_water_load_pipeline_state.clear()"),
		2,
		"loader retains two clear sites",
		failures
	)


func _verify_bounded_timing(failures: Array[String]) -> float:
	var pipeline: RefCounted = PIPELINE_SCRIPT.new()
	var started_microseconds := Time.get_ticks_usec()
	for iteration in range(100):
		for tile_index in range(256):
			var tile_key := "tile:%d" % tile_index
			pipeline.call("enqueue_request", tile_key, "res://tile_%d.adt" % tile_index)
		while int(pipeline.call("pending_request_count")) > 0:
			var request: Dictionary = pipeline.call("take_next_request")
			var tile_key := String(request["key"])
			pipeline.call("remember_active_task", tile_key, tile_key.hash())
			pipeline.call("publish_result", tile_key, String(request["path"]), {})
		while pipeline.call("has_result"):
			var result: Dictionary = pipeline.call("pop_result")
			pipeline.call("complete_active_task", String(result["key"]))
		pipeline.call("clear")
		_expect_equal(
			int(pipeline.call("total_load_count")),
			0,
			"timing cycle %d clears" % iteration,
			failures
		)
	var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
	_expect_true(
		elapsed_milliseconds < 1000.0,
		"100 request/task/result cycles 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 _count_occurrences(text: String, token: String) -> int:
	return text.split(token).size() - 1


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_string_equal(
		actual_value: String,
		expected_value: String,
		label: String,
		failures: Array[String]
) -> void:
	if actual_value != expected_value:
		failures.append("%s expected %s, got %s" % [label, expected_value, actual_value])


func _expect_string_array(
		actual_values: Array,
		expected_values: Array,
		label: String,
		failures: Array[String]
) -> void:
	if actual_values != expected_values:
		failures.append("%s expected %s, got %s" % [label, expected_values, actual_values])


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


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