extends SceneTree

## Test suite for ValueStepper component
##
## Tests data loading, value navigation, and input handling.

const TestHelperClass = preload("res://tests/helpers/TestHelper.gd")

var stepper_scene: PackedScene
var stepper_instance: Control
var test_viewport: SubViewport
var original_language: String


func _initialize():
	# Wait for autoloads to initialize
	await process_frame
	await process_frame

	run_tests()

	# Exit after tests complete
	quit()


func run_tests():
	TestHelperClass.print_test_header("ValueStepper Component")

	# Store original settings
	var settings_manager = root.get_node("SettingsManager")
	original_language = settings_manager.get_setting("language")

	# Setup test environment
	setup_test_environment()

	# Run test suites
	test_basic_functionality()
	test_data_source_loading()
	test_value_navigation()
	test_custom_values()
	test_input_handling()
	test_visual_feedback()
	test_settings_integration()
	test_boundary_conditions()
	test_error_handling()

	# Cleanup
	cleanup_tests()

	TestHelperClass.print_test_footer("ValueStepper Component")


func setup_test_environment():
	TestHelperClass.print_step("Test Environment Setup")

	# Load ValueStepper scene
	stepper_scene = load("res://scenes/ui/components/ValueStepper.tscn")
	TestHelperClass.assert_not_null(stepper_scene, "ValueStepper scene loads successfully")

	# Create test viewport for isolated testing
	test_viewport = SubViewport.new()
	test_viewport.size = Vector2i(400, 200)
	root.add_child(test_viewport)

	# Instance ValueStepper in test viewport
	if stepper_scene:
		stepper_instance = stepper_scene.instantiate()
		test_viewport.add_child(stepper_instance)
		TestHelperClass.assert_not_null(stepper_instance, "ValueStepper instance created successfully")

		# Wait for initialization
		await process_frame
		await process_frame


func test_basic_functionality():
	TestHelperClass.print_step("Basic Functionality")

	if not stepper_instance:
		TestHelperClass.assert_true(false, "ValueStepper instance not available for testing")
		return

	# Test that ValueStepper has expected properties
	var expected_properties = [
		"data_source", "custom_format_function", "values", "display_names", "current_index"
	]
	for prop in expected_properties:
		TestHelperClass.assert_true(prop in stepper_instance, "ValueStepper has property: " + prop)

	# Test that ValueStepper has expected methods
	var expected_methods = [
		"change_value",
		"setup_custom_values",
		"get_current_value",
		"set_current_value",
		"set_highlighted",
		"handle_input_action",
		"get_control_name"
	]
	TestHelperClass.assert_has_methods(
		stepper_instance, expected_methods, "ValueStepper component methods"
	)

	# Test signals
	TestHelperClass.assert_true(
		stepper_instance.has_signal("value_changed"), "ValueStepper has value_changed signal"
	)

	# Test UI components
	TestHelperClass.assert_not_null(stepper_instance.left_button, "Left button is available")
	TestHelperClass.assert_not_null(stepper_instance.right_button, "Right button is available")
	TestHelperClass.assert_not_null(stepper_instance.value_display, "Value display label is available")

	# Test UI component types
	TestHelperClass.assert_true(stepper_instance.left_button is Button, "Left button is Button type")
	TestHelperClass.assert_true(stepper_instance.right_button is Button, "Right button is Button type")
	TestHelperClass.assert_true(stepper_instance.value_display is Label, "Value display is Label type")


func test_data_source_loading():
	TestHelperClass.print_step("Data Source Loading")

	if not stepper_instance:
		return

	# Test default language data source
	TestHelperClass.assert_equal(
		"language", stepper_instance.data_source, "Default data source is language"
	)

	# Test that values are loaded
	TestHelperClass.assert_not_null(stepper_instance.values, "Values array is initialized")
	TestHelperClass.assert_not_null(stepper_instance.display_names, "Display names array is initialized")
	TestHelperClass.assert_true(stepper_instance.values is Array, "Values is Array type")
	TestHelperClass.assert_true(stepper_instance.display_names is Array, "Display names is Array type")

	# Test that language data is loaded correctly
	if stepper_instance.data_source == "language":
		TestHelperClass.assert_true(stepper_instance.values.size() > 0, "Language values loaded")
		TestHelperClass.assert_true(
			stepper_instance.display_names.size() > 0, "Language display names loaded"
		)
		TestHelperClass.assert_equal(
			stepper_instance.values.size(),
			stepper_instance.display_names.size(),
			"Values and display names arrays have same size"
		)

		# Test that current language is properly selected
		var current_lang = root.get_node("SettingsManager").get_setting("language")
		var expected_index = stepper_instance.values.find(current_lang)
		if expected_index >= 0:
			TestHelperClass.assert_equal(
				expected_index,
				stepper_instance.current_index,
				"Current language index set correctly"
			)

	# Test resolution data source
	var resolution_stepper = stepper_scene.instantiate()
	resolution_stepper.data_source = "resolution"
	test_viewport.add_child(resolution_stepper)
	await process_frame

	TestHelperClass.assert_true(resolution_stepper.values.size() > 0, "Resolution values loaded")
	TestHelperClass.assert_contains(
		resolution_stepper.values, "1920x1080", "Resolution data contains expected value"
	)

	resolution_stepper.queue_free()

	# Test difficulty data source
	var difficulty_stepper = stepper_scene.instantiate()
	difficulty_stepper.data_source = "difficulty"
	test_viewport.add_child(difficulty_stepper)
	await process_frame

	TestHelperClass.assert_true(difficulty_stepper.values.size() > 0, "Difficulty values loaded")
	TestHelperClass.assert_contains(
		difficulty_stepper.values, "normal", "Difficulty data contains expected value"
	)
	TestHelperClass.assert_equal(1, difficulty_stepper.current_index, "Difficulty defaults to normal")

	difficulty_stepper.queue_free()


func test_value_navigation():
	TestHelperClass.print_step("Value Navigation")

	if not stepper_instance:
		return

	# Store original state
	var original_index = stepper_instance.current_index
	var _original_value = stepper_instance.get_current_value()

	# Test forward navigation
	var initial_value = stepper_instance.get_current_value()
	stepper_instance.change_value(1)
	var next_value = stepper_instance.get_current_value()
	TestHelperClass.assert_not_equal(initial_value, next_value, "Forward navigation changes value")

	# Test backward navigation
	stepper_instance.change_value(-1)
	var back_value = stepper_instance.get_current_value()
	TestHelperClass.assert_equal(
		initial_value, back_value, "Backward navigation returns to original value"
	)

	# Test wrap-around forward
	var max_index = stepper_instance.values.size() - 1
	stepper_instance.current_index = max_index
	stepper_instance.change_value(1)
	TestHelperClass.assert_equal(
		0, stepper_instance.current_index, "Forward navigation wraps to beginning"
	)

	# Test wrap-around backward
	stepper_instance.current_index = 0
	stepper_instance.change_value(-1)
	TestHelperClass.assert_equal(
		max_index, stepper_instance.current_index, "Backward navigation wraps to end"
	)

	# Restore original state
	stepper_instance.current_index = original_index
	stepper_instance._update_display()


func test_custom_values():
	TestHelperClass.print_step("Custom Values")

	if not stepper_instance:
		return

	# Store original state
	var original_values = stepper_instance.values.duplicate()
	var original_display_names = stepper_instance.display_names.duplicate()
	var original_index = stepper_instance.current_index

	# Test custom values without display names
	var custom_values = ["apple", "banana", "cherry"]
	stepper_instance.setup_custom_values(custom_values)

	TestHelperClass.assert_equal(3, stepper_instance.values.size(), "Custom values set correctly")
	TestHelperClass.assert_equal("apple", stepper_instance.values[0], "First custom value correct")
	TestHelperClass.assert_equal(0, stepper_instance.current_index, "Index reset to 0 for custom values")
	TestHelperClass.assert_equal(
		"apple", stepper_instance.get_current_value(), "Current value matches first custom value"
	)

	# Test custom values with display names
	var custom_display_names = ["Red Apple", "Yellow Banana", "Red Cherry"]
	stepper_instance.setup_custom_values(custom_values, custom_display_names)

	TestHelperClass.assert_equal(
		3, stepper_instance.display_names.size(), "Custom display names set correctly"
	)
	TestHelperClass.assert_equal(
		"Red Apple", stepper_instance.display_names[0], "First display name correct"
	)

	# Test navigation with custom values
	stepper_instance.change_value(1)
	TestHelperClass.assert_equal(
		"banana", stepper_instance.get_current_value(), "Navigation works with custom values"
	)

	# Test set_current_value
	stepper_instance.set_current_value("cherry")
	TestHelperClass.assert_equal(
		"cherry", stepper_instance.get_current_value(), "set_current_value works correctly"
	)
	TestHelperClass.assert_equal(
		2, stepper_instance.current_index, "Index updated correctly by set_current_value"
	)

	# Test invalid value
	stepper_instance.set_current_value("grape")
	TestHelperClass.assert_equal(
		"cherry", stepper_instance.get_current_value(), "Invalid value doesn't change current value"
	)

	# Restore original state
	stepper_instance.values = original_values
	stepper_instance.display_names = original_display_names
	stepper_instance.current_index = original_index
	stepper_instance._update_display()


func test_input_handling():
	TestHelperClass.print_step("Input Handling")

	if not stepper_instance:
		return

	# Store original state
	var original_value = stepper_instance.get_current_value()

	# Test left input action
	var left_handled = stepper_instance.handle_input_action("move_left")
	TestHelperClass.assert_true(left_handled, "Left input action handled")
	TestHelperClass.assert_not_equal(
		original_value, stepper_instance.get_current_value(), "Left action changes value"
	)

	# Test right input action
	var right_handled = stepper_instance.handle_input_action("move_right")
	TestHelperClass.assert_true(right_handled, "Right input action handled")
	TestHelperClass.assert_equal(
		original_value,
		stepper_instance.get_current_value(),
		"Right action returns to original value"
	)

	# Test invalid input action
	var invalid_handled = stepper_instance.handle_input_action("invalid_action")
	TestHelperClass.assert_false(invalid_handled, "Invalid input action not handled")

	# Test button press simulation
	if stepper_instance.left_button:
		var before_left = stepper_instance.get_current_value()
		stepper_instance._on_left_button_pressed()
		TestHelperClass.assert_not_equal(
			before_left, stepper_instance.get_current_value(), "Left button press changes value"
		)

	if stepper_instance.right_button:
		var _before_right = stepper_instance.get_current_value()
		stepper_instance._on_right_button_pressed()
		TestHelperClass.assert_equal(
			original_value,
			stepper_instance.get_current_value(),
			"Right button press returns to original"
		)


func test_visual_feedback():
	TestHelperClass.print_step("Visual Feedback")

	if not stepper_instance:
		return

	# Store original visual properties
	var original_scale = stepper_instance.scale
	var original_modulate = stepper_instance.modulate

	# Test highlighting
	stepper_instance.set_highlighted(true)
	TestHelperClass.assert_true(stepper_instance.is_highlighted, "Highlighted state set correctly")
	TestHelperClass.assert_true(
		stepper_instance.scale.x > original_scale.x, "Scale increased when highlighted"
	)

	# Test unhighlighting
	stepper_instance.set_highlighted(false)
	TestHelperClass.assert_false(stepper_instance.is_highlighted, "Highlighted state cleared correctly")
	TestHelperClass.assert_equal(
		original_scale, stepper_instance.scale, "Scale restored when unhighlighted"
	)
	TestHelperClass.assert_equal(
		original_modulate, stepper_instance.modulate, "Modulate restored when unhighlighted"
	)

	# Test display update
	if stepper_instance.value_display:
		var current_text = stepper_instance.value_display.text
		TestHelperClass.assert_true(current_text.length() > 0, "Value display has text content")
		TestHelperClass.assert_not_equal("N/A", current_text, "Value display shows valid content")


func test_settings_integration():
	TestHelperClass.print_step("Settings Integration")

	if not stepper_instance or stepper_instance.data_source != "language":
		return

	# Store original language
	var original_lang = root.get_node("SettingsManager").get_setting("language")

	# Test that changing language stepper updates settings
	var available_languages = stepper_instance.values
	if available_languages.size() > 1:
		# Find a different language
		var target_lang = null
		for lang in available_languages:
			if lang != original_lang:
				target_lang = lang
				break

		if target_lang:
			stepper_instance.set_current_value(target_lang)
			stepper_instance._apply_value_change(target_lang, stepper_instance.current_index)

			# Verify setting was updated
			var updated_lang = root.get_node("SettingsManager").get_setting("language")
			TestHelperClass.assert_equal(target_lang, updated_lang, "Language setting updated correctly")

			# Restore original language
			root.get_node("SettingsManager").set_setting("language", original_lang)


func test_boundary_conditions():
	TestHelperClass.print_step("Boundary Conditions")

	if not stepper_instance:
		return

	# Test empty values array
	var empty_stepper = stepper_scene.instantiate()
	empty_stepper.data_source = "unknown"  # Will result in empty arrays
	test_viewport.add_child(empty_stepper)
	await process_frame

	TestHelperClass.assert_equal(
		"", empty_stepper.get_current_value(), "Empty values array returns empty string"
	)

	# Test change_value with empty array
	empty_stepper.change_value(1)  # Should not crash
	TestHelperClass.assert_true(true, "change_value handles empty array gracefully")

	empty_stepper.queue_free()

	# Test index bounds
	if stepper_instance.values.size() > 0:
		# Test negative index handling
		stepper_instance.current_index = -1
		stepper_instance._update_display()
		TestHelperClass.assert_equal(
			"N/A", stepper_instance.value_display.text, "Negative index shows N/A"
		)

		# Test out-of-bounds index handling
		stepper_instance.current_index = stepper_instance.values.size()
		stepper_instance._update_display()
		TestHelperClass.assert_equal(
			"N/A", stepper_instance.value_display.text, "Out-of-bounds index shows N/A"
		)

		# Restore valid index
		stepper_instance.current_index = 0
		stepper_instance._update_display()


func test_error_handling():
	TestHelperClass.print_step("Error Handling")

	if not stepper_instance:
		return

	# Test unknown data source
	var unknown_stepper = stepper_scene.instantiate()
	unknown_stepper.data_source = "invalid_source"
	test_viewport.add_child(unknown_stepper)
	await process_frame

	# Should not crash and should handle gracefully
	TestHelperClass.assert_true(true, "Unknown data source handled gracefully")
	unknown_stepper.queue_free()

	# Test get_control_name
	var control_name = stepper_instance.get_control_name()
	TestHelperClass.assert_true(control_name.ends_with("_stepper"), "Control name has correct suffix")
	TestHelperClass.assert_true(
		control_name.begins_with(stepper_instance.data_source), "Control name includes data source"
	)

	# Test custom values with mismatched arrays
	var values_3 = ["a", "b", "c"]
	var names_2 = ["A", "B"]
	stepper_instance.setup_custom_values(values_3, names_2)

	# Should handle gracefully - display_names should be duplicated from values
	TestHelperClass.assert_equal(3, stepper_instance.values.size(), "Values array size preserved")
	TestHelperClass.assert_equal(
		2, stepper_instance.display_names.size(), "Display names size preserved as provided"
	)

	# Test navigation with mismatched arrays
	stepper_instance.current_index = 2  # Index where display_names doesn't exist
	stepper_instance._update_display()
	TestHelperClass.assert_equal(
		"c", stepper_instance.value_display.text, "Falls back to value when display name missing"
	)


func cleanup_tests():
	TestHelperClass.print_step("Cleanup")

	# Restore original language setting
	root.get_node("SettingsManager").set_setting("language", original_language)

	# Clean up stepper instance
	if stepper_instance and is_instance_valid(stepper_instance):
		stepper_instance.queue_free()

	# Clean up test viewport
	if test_viewport and is_instance_valid(test_viewport):
		test_viewport.queue_free()

	# Wait for cleanup
	await process_frame

	TestHelperClass.assert_true(true, "Test cleanup completed")
