extends Node

## The prologue is one authored scene of slides and captions (ligne 94).
## French shows the captions written in Déroulé, never an older translation;
## English reads `ui.intro.<step>`.
const INTRO := preload("res://scenes/main/intro_comic.tscn")
const IntroComic := preload("res://scripts/core/intro_comic.gd")
var failures: Array[String] = []

func _ready() -> void:
	GameState.save_locked = true
	call_deferred("_run")

func _check(condition: bool, message: String) -> void:
	if not condition:
		failures.append(message)
		push_error("INTRO CHECK: " + message)

func _run() -> void:
	_check(DataCatalog.loaded_from_master, DataCatalog.last_load_error)
	if not DataCatalog.loaded_from_master:
		get_tree().quit(1)
		return
	var definition: Dictionary = DataCatalog.STORY_SCENES.get(IntroComic.SCENE_ID, {})
	_check(not definition.is_empty(), "The intro scene is missing from the master")
	var authored := {}
	var slide_count := 0
	for event: Dictionary in definition.get("events", []):
		if event.type == "text":
			authored[str(event.id)] = str(event.text)
		elif event.type == "slide":
			slide_count += 1
	_check(slide_count > 1 and not authored.is_empty(), "The intro has no slides or no captions")
	# The former bug, in memory only: a stale French entry must not hide Déroulé.
	var stale_key := "ui.intro." + str(authored.keys()[0])
	var saved_entry: Dictionary = Loc.ENTRIES.get(stale_key, {})
	var stale := saved_entry.duplicate()
	stale["fr"] = "Ancienne version."
	Loc.ENTRIES[stale_key] = stale
	var initial_payload: Dictionary = GameState.call("_build_save_payload").duplicate(true)
	initial_payload.erase("saved_at_unix")
	var original_language := Loc.language
	Loc.set_language("fr")
	var preview := INTRO.instantiate()
	add_child(preview)
	await get_tree().process_frame
	await get_tree().process_frame
	var story: Node2D = preview.get("story")
	_check(bool(story.get("running")), "Preview does not start")
	var slides: Control = story.get("_slides")
	_check(slides != null and bool(slides.get("transitioning")), "Opening fade is missing")
	if slides == null:
		_finish(preview, original_language)
		return
	_check((slides.get("current") as CanvasItem).modulate.a < 0.2, "Opening image appears without fading")
	await get_tree().create_timer(0.65).timeout
	var midpoint: float = (slides.get("current") as CanvasItem).modulate.a
	_check(midpoint > 0.15 and midpoint < 0.8, "Opening fade does not progress across 1.5 seconds")
	var seen := {}
	var captions := {}
	for step: int in 30:
		if bool(preview.get("finished")):
			break
		await _wait_for_caption(story)
		var chapter: int = int(preview.get("chapter_index"))
		captions[str(story.get("current_event_id"))] = (story.get("_text_label") as Label).text
		var current := slides.get("current") as VisualHolder
		_check(current.is_visible_in_tree() and current.is_showing(), "Comic art is hidden under its caption")
		_check(current.get_child_count() > 0, "Comic texture did not load")
		_check((preview.get("_status") as Label).text.ends_with("%02d" % (chapter + 1)), "Chapter counter does not follow the slides")
		if not seen.has(chapter) and OS.get_cmdline_user_args().has("--capture"):
			await RenderingServer.frame_post_draw
			get_viewport().get_texture().get_image().save_png("user://intro_preview_%d.png" % chapter)
		seen[chapter] = true
		if step == 0:
			var click := InputEventMouseButton.new()
			click.button_index = MOUSE_BUTTON_LEFT
			click.position = Vector2(960, 900)
			click.pressed = true
			Input.parse_input_event(click)
		else:
			var advance := InputEventAction.new()
			advance.action = "action"
			advance.pressed = true
			Input.parse_input_event(advance)
		await get_tree().process_frame
		var event := InputEventAction.new()
		event.action = "action"
		event.pressed = false
		Input.parse_input_event(event)
		await get_tree().process_frame
		if step == 0:
			_check(bool(slides.get("transitioning")) and (slides.get("previous") as CanvasItem).visible, "Slides cut through black instead of crossfading")
	_check(seen.size() == slide_count, "Not every authored slide was reached (%d / %d)" % [seen.size(), slide_count])
	_check(captions == authored, "French captions differ from Déroulé: expected %s, read %s" % [authored, captions])
	_check(bool(preview.get("finished")), "The intro did not finish")
	_check(not GameState.scene_seen(IntroComic.SCENE_ID), "The intro would be remembered as seen")
	preview.call("_press", "language")
	await get_tree().process_frame
	_check(Loc.language == "en", "Language replay did not switch to English")
	await _wait_for_caption(story)
	var first_id := str(story.get("current_event_id"))
	var english := str((Loc.ENTRIES.get("ui.intro." + first_id, {}) as Dictionary).get("en", ""))
	_check(not english.is_empty(), "The first caption has no English translation")
	_check((story.get("_text_label") as Label).text == english, "English caption is not the translation")
	var skip_event := InputEventAction.new()
	skip_event.action = "pause"
	skip_event.pressed = true
	Input.parse_input_event(skip_event)
	await get_tree().process_frame
	_check(bool(preview.get("finished")) and int(preview.get("chapter_index")) == 0, "Skip must leave the entire intro")
	var final_payload: Dictionary = GameState.call("_build_save_payload").duplicate(true)
	final_payload.erase("saved_at_unix")
	_check(initial_payload == final_payload, "Preview changed persistent gameplay state")
	Loc.ENTRIES[stale_key] = saved_entry
	_finish(preview, original_language)

func _finish(preview: Node, original_language: String) -> void:
	preview.queue_free()
	await get_tree().process_frame
	_check(Loc.language == "fr", "Preview did not restore its entry language")
	Loc.set_language(original_language)
	print("INTRO CHECK: ", "OK" if failures.is_empty() else failures)
	get_tree().quit(0 if failures.is_empty() else 1)

func _wait_for_caption(story: Node2D) -> void:
	# Le fondu, puis la légende s'écrit avant d'attendre (ligne 72) : du temps.
	var deadline := Time.get_ticks_msec() + 15000
	while bool(story.get("running")) and not bool(story.get("_waiting_continue")) and Time.get_ticks_msec() < deadline:
		await get_tree().process_frame
	_check(bool(story.get("_waiting_continue")), "Slide never becomes readable after its fade")
