extends SceneTree
## Headless focus tests for the dev console — verifies the command line keeps (or
## reclaims) keyboard focus after the actions that were observed to drop it.
## Run:  godot --headless --script tests/console_focus_test.gd
##
## Exit code 0 = all passed, 1 = any failure.

var _passed: int = 0
var _failed: int = 0


func _init() -> void:
	_run.call_deferred()


func _run() -> void:
	print("=".repeat(60))
	print("CONSOLE FOCUS TESTS")
	print("=".repeat(60))

	await test_focus_after_submit()
	await test_focus_after_external_steal()

	print("=".repeat(60))
	print("Results: %d passed, %d failed" % [_passed, _failed])
	print("=".repeat(60))

	quit(1 if _failed > 0 else 0)


func _assert_true(condition: bool, desc: String) -> void:
	if condition:
		_passed += 1
		print("  PASS: %s" % desc)
	else:
		_failed += 1
		print("  FAIL: %s" % desc)


func _get_console() -> Node:
	var c: Node = root.get_node_or_null("/root/Console")
	if c == null:
		_assert_true(false, "Console autoload should exist")
	return c


# Drive the real path: open via _toggle (pauses the tree, slide tween grabs focus),
# type a command as real key events, submit with a real Enter, and confirm the guard
# keeps the command line focused across the paused submit.
func test_focus_after_submit() -> void:
	print("\n-- test_focus_after_submit --")
	var menu := _get_console()
	if menu == null:
		return

	menu._toggle()
	# Let the open slide tween finish and its grab_focus callback fire.
	for _i in 20:
		await process_frame
	_assert_true(paused, "tree paused while console open")
	_assert_true(menu._input_field.has_focus(), "input has focus after open")

	menu._input_field.text = "clear"
	menu._input_field.caret_column = 5
	_press(KEY_ENTER)
	# The submit and any focus theft settle over the next few idle frames.
	for _i in 5:
		await process_frame
	_assert_true(menu._input_field.has_focus(), "input still has focus after submit")

	menu._toggle()
	for _i in 20:
		await process_frame


# Push a key down+up through the viewport so LineEdit / _input handle it for real.
func _press(keycode: Key) -> void:
	var down := InputEventKey.new()
	down.keycode = keycode
	down.physical_keycode = keycode
	down.pressed = true
	root.push_input(down)
	var up := InputEventKey.new()
	up.keycode = keycode
	up.physical_keycode = keycode
	up.pressed = false
	root.push_input(up)


# Simulate anything yanking focus away mid-session (a stray click, a UI button); the
# guard in _process must pull it straight back while the console is open.
func test_focus_after_external_steal() -> void:
	print("\n-- test_focus_after_external_steal --")
	var menu := _get_console()
	if menu == null:
		return

	menu._is_open = true
	menu._panel.visible = true
	menu._input_field.grab_focus()
	await process_frame

	menu._input_field.release_focus()
	_assert_true(not menu._input_field.has_focus(), "focus released (precondition)")

	await process_frame
	_assert_true(menu._input_field.has_focus(), "guard reclaims focus next frame")

	menu._is_open = false
	menu._panel.visible = false
