extends SceneTree

const MANIFEST_PATH := "res://src/tools/render_baseline_manifest.json"
const STREAMING_LOADER := preload("res://src/scenes/streaming/streaming_world_loader.gd")
const STREAMING_TILE := preload("res://src/resources/streaming_adt_tile.gd")
const SPLAT_TILE := preload("res://src/resources/splat_adt_tile.gd")
const CONTROL_SPLAT_TILE := preload("res://src/resources/control_splat_adt_tile.gd")
const WMO_STREAMING := preload("res://src/resources/wmo_streaming_resource.gd")
const WMO_BUILDER := preload("res://addons/mpq_extractor/loaders/wmo_builder.gd")
const M2_BUILDER := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")


func _initialize() -> void:
	var manifest := _load_manifest()
	if manifest.is_empty():
		quit(1)
		return

	var failures: Array[String] = []
	_expect(int(manifest.get("schema_version", 0)) == 1, "manifest schema must be version 1", failures)
	_expect(String(manifest.get("profile", "")) == "Blizzlike335", "manifest profile must be Blizzlike335", failures)
	_expect(ResourceLoader.exists(String(manifest.get("scene", ""))), "streaming scene must exist", failures)
	var fidelity_viewport: Array = manifest.get("fidelity_comparison_viewport", [])
	_expect(fidelity_viewport.size() == 2 and int(fidelity_viewport[0]) > 0 and int(fidelity_viewport[1]) > 0, "fidelity comparison viewport must contain positive width and height", failures)

	var covered := {}
	var names := {}
	for checkpoint_variant in manifest.get("checkpoints", []):
		if not (checkpoint_variant is Dictionary):
			failures.append("checkpoint entry must be a dictionary")
			continue
		var checkpoint: Dictionary = checkpoint_variant
		var checkpoint_name := String(checkpoint.get("name", ""))
		_expect(not checkpoint_name.is_empty(), "checkpoint name must not be empty", failures)
		_expect(not names.has(checkpoint_name), "duplicate checkpoint name: %s" % checkpoint_name, failures)
		names[checkpoint_name] = true
		for key in ["camera", "target", "player"]:
			var value = checkpoint.get(key, [])
			_expect(value is Array and value.size() == 3, "%s.%s must contain three coordinates" % [checkpoint_name, key], failures)
		for coverage_variant in checkpoint.get("coverage", []):
			covered[String(coverage_variant)] = true

	for required_variant in manifest.get("required_coverage", []):
		var required := String(required_variant)
		_expect(covered.has(required), "missing checkpoint coverage: %s" % required, failures)

	var comparison_budgets: Dictionary = manifest.get("comparison_budgets", {})
	for threshold_name in ["visual_mean_perceptual_error_max", "visual_changed_pixel_ratio_max", "visual_pixel_error_threshold"]:
		var threshold := float(comparison_budgets.get(threshold_name, -1.0))
		_expect(threshold >= 0.0 and threshold <= 1.0, "%s must be between 0 and 1" % threshold_name, failures)

	var contract: Dictionary = manifest.get("cache_contract", {})
	var actual_versions := {
		"baked_terrain": STREAMING_LOADER.REQUIRED_BAKED_TILE_FORMAT_VERSION,
		"streaming_terrain": STREAMING_TILE.FORMAT_VERSION,
		"terrain_splat": SPLAT_TILE.FORMAT_VERSION,
		"terrain_control_splat": CONTROL_SPLAT_TILE.FORMAT_VERSION,
		"wmo_streaming": WMO_STREAMING.FORMAT_VERSION,
		"wmo_builder": WMO_BUILDER.WMO_BUILDER_FORMAT_VERSION,
		"m2_material": M2_BUILDER.MATERIAL_FORMAT_VERSION,
	}
	for cache_name in actual_versions:
		var entry: Dictionary = contract.get(cache_name, {})
		_expect(int(entry.get("version", -1)) == int(actual_versions[cache_name]), "cache version mismatch for %s" % cache_name, failures)
		_expect(not String(entry.get("invalidate", "")).is_empty(), "cache invalidation rule missing for %s" % cache_name, failures)

	if not failures.is_empty():
		for failure in failures:
			push_error("RENDER_BASELINE_MANIFEST: %s" % failure)
		quit(1)
		return
	print("RENDER_BASELINE_MANIFEST PASS checkpoints=%d coverage=%d caches=%d" % [names.size(), covered.size(), actual_versions.size()])
	quit(0)


func _load_manifest() -> Dictionary:
	var file := FileAccess.open(MANIFEST_PATH, FileAccess.READ)
	if file == null:
		push_error("Cannot open render baseline manifest: %s" % MANIFEST_PATH)
		return {}
	var parsed = JSON.parse_string(file.get_as_text())
	if not (parsed is Dictionary):
		push_error("Render baseline manifest is not a JSON object")
		return {}
	return parsed


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