class_name M2MeshResourceFinalizer
extends RefCounted

## Polls static M2 ResourceLoader requests and publishes prepared Mesh outcomes.
## The caller owns permits, pipeline/cache lifetime and MultiMesh materialization.

var _mesh_resource_extractor: RefCounted
var _runtime_mesh_finalizer: RefCounted
var _raw_model_repository: RefCounted
var _resource_loader_adapter: Object


func _init(
	mesh_resource_extractor: RefCounted,
	runtime_mesh_finalizer: RefCounted,
	raw_model_repository: RefCounted,
	resource_loader_adapter: Object = null
) -> void:
	_mesh_resource_extractor = mesh_resource_extractor
	_runtime_mesh_finalizer = runtime_mesh_finalizer
	_raw_model_repository = raw_model_repository
	_resource_loader_adapter = resource_loader_adapter


## Moves terminal threaded requests into the pipeline finalize FIFO.
## Empty resource paths retain the historical immediate missing-model outcome.
func poll_terminal_requests(
	load_pipeline_state: RefCounted,
	prototype_cache_state: RefCounted
) -> int:
	if load_pipeline_state == null or prototype_cache_state == null:
		return 0
	var completed_request_count := 0
	var request_records: Array = load_pipeline_state.call(
		"request_records_snapshot"
	)
	for request_variant in request_records:
		if not (request_variant is Dictionary):
			continue
		var request: Dictionary = request_variant
		var normalized_relative_path := String(request.get("normalized", ""))
		var resource_path := String(request.get("path", ""))
		if resource_path.is_empty():
			load_pipeline_state.call(
				"discard_request",
				normalized_relative_path
			)
			prototype_cache_state.call(
				"mark_model_missing",
				normalized_relative_path
			)
			completed_request_count += 1
			continue
		var load_status := load_threaded_get_status(resource_path)
		if (
			load_status != ResourceLoader.THREAD_LOAD_LOADED
			and load_status != ResourceLoader.THREAD_LOAD_FAILED
		):
			continue
		load_pipeline_state.call(
			"complete_request",
			normalized_relative_path,
			load_status
		)
		completed_request_count += 1
	return completed_request_count


## Pops and finalizes one terminal record. A true result means a record was
## consumed, including cached, failed and invalid outcomes.
func finalize_next_resource(
	load_pipeline_state: RefCounted,
	mesh_resource_cache_state: RefCounted,
	prototype_cache_state: RefCounted,
	extracted_directory: String
) -> bool:
	if (
		load_pipeline_state == null
		or mesh_resource_cache_state == null
		or prototype_cache_state == null
	):
		return false
	var terminal_record: Dictionary = load_pipeline_state.call(
		"pop_finalize_record"
	)
	if terminal_record.is_empty():
		return false
	var normalized_relative_path := String(
		terminal_record.get("normalized", "")
	)
	if (
		normalized_relative_path.is_empty()
		or bool(mesh_resource_cache_state.call(
			"has_mesh",
			normalized_relative_path
		))
	):
		return true
	if int(terminal_record.get(
		"status",
		ResourceLoader.THREAD_LOAD_FAILED
	)) != ResourceLoader.THREAD_LOAD_LOADED:
		prototype_cache_state.call(
			"mark_model_missing",
			normalized_relative_path
		)
		return true

	var resource_path := String(terminal_record.get("path", ""))
	var loaded_resource := load_threaded_get(resource_path)
	var mesh := _mesh_resource_extractor.call(
		"extract_first_mesh",
		loaded_resource
	) as Mesh
	if mesh == null:
		prototype_cache_state.call(
			"mark_model_missing",
			normalized_relative_path
		)
		return true
	mesh_resource_cache_state.call(
		"store_mesh",
		normalized_relative_path,
		prepare_mesh_for_runtime(
			normalized_relative_path,
			mesh,
			extracted_directory
		)
	)
	return true


## Applies the existing refresh-version/raw-data/rebuild path to one Mesh.
func prepare_mesh_for_runtime(
	normalized_relative_path: String,
	mesh: Mesh,
	extracted_directory: String
) -> Mesh:
	if mesh == null:
		return null
	if (
		_runtime_mesh_finalizer == null
		or not bool(_runtime_mesh_finalizer.call(
			"requires_raw_data_for_refresh",
			mesh
		))
	):
		return mesh
	var raw_model_data: Dictionary = {}
	if _raw_model_repository != null:
		raw_model_data = _raw_model_repository.call(
			"load_static_model_data",
			extracted_directory,
			normalized_relative_path
		)
	return _runtime_mesh_finalizer.call(
		"finalize_mesh",
		normalized_relative_path,
		mesh,
		raw_model_data,
		extracted_directory
	) as Mesh


## Production ResourceLoader status adapter; injectable in synthetic tests.
func load_threaded_get_status(resource_path: String) -> int:
	if _resource_loader_adapter != null:
		return int(_resource_loader_adapter.call(
			"load_threaded_get_status",
			resource_path
		))
	return ResourceLoader.load_threaded_get_status(resource_path)


## Production ResourceLoader terminal-result adapter; injectable in tests.
func load_threaded_get(resource_path: String) -> Resource:
	if _resource_loader_adapter != null:
		return _resource_loader_adapter.call(
			"load_threaded_get",
			resource_path
		) as Resource
	return ResourceLoader.load_threaded_get(resource_path)
