extends SceneTree

## Comprehensive test suite for AudioManager
##
## Tests audio resource loading, stream configuration, volume management,
## audio bus configuration, and playback control functionality.
## Validates proper audio system initialization and error handling.

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

var audio_manager: Node
var original_music_volume: float
var original_sfx_volume: float


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("AudioManager")

	# Get reference to AudioManager
	audio_manager = root.get_node("AudioManager")
	if not audio_manager:
		TestHelperClass.assert_true(false, "AudioManager autoload not found")
		TestHelperClass.print_test_footer("AudioManager")
		return

	# Store original settings for restoration
	var settings_manager = root.get_node("SettingsManager")
	original_music_volume = settings_manager.get_setting("music_volume")
	original_sfx_volume = settings_manager.get_setting("sfx_volume")

	# Run test suites
	test_basic_functionality()
	test_audio_constants()
	test_audio_player_initialization()
	test_stream_loading_and_validation()
	test_audio_bus_configuration()
	test_volume_management()
	test_music_playback_control()
	test_ui_sound_effects()
	test_stream_loop_configuration()
	test_error_handling()

	# Cleanup and restore original state
	cleanup_tests()

	TestHelperClass.print_test_footer("AudioManager")


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

	# Test that AudioManager has expected properties
	TestHelperClass.assert_has_properties(
		audio_manager,
		["music_player", "ui_click_player", "click_stream"],
		"AudioManager properties"
	)

	# Test that AudioManager has expected methods
	var expected_methods = ["update_music_volume", "play_ui_click"]
	TestHelperClass.assert_has_methods(audio_manager, expected_methods, "AudioManager methods")

	# Test that AudioManager has expected constants
	TestHelperClass.assert_true("MUSIC_PATH" in audio_manager, "MUSIC_PATH constant exists")
	TestHelperClass.assert_true(
		"UI_CLICK_SOUND_PATH" in audio_manager, "UI_CLICK_SOUND_PATH constant exists"
	)


func test_audio_constants():
	TestHelperClass.print_step("Audio File Constants")

	# Test path format validation
	var music_path = audio_manager.MUSIC_PATH
	var click_path = audio_manager.UI_CLICK_SOUND_PATH

	TestHelperClass.assert_true(music_path.begins_with("res://"), "Music path uses res:// protocol")
	TestHelperClass.assert_true(
		click_path.begins_with("res://"), "Click sound path uses res:// protocol"
	)

	# Test file extensions
	var valid_audio_extensions = [".wav", ".ogg", ".mp3"]
	var music_has_valid_ext = false
	var click_has_valid_ext = false

	for ext in valid_audio_extensions:
		if music_path.ends_with(ext):
			music_has_valid_ext = true
		if click_path.ends_with(ext):
			click_has_valid_ext = true

	TestHelperClass.assert_true(music_has_valid_ext, "Music file has valid audio extension")
	TestHelperClass.assert_true(click_has_valid_ext, "Click sound has valid audio extension")

	# Test that audio files exist
	TestHelperClass.assert_true(ResourceLoader.exists(music_path), "Music file exists at path")
	TestHelperClass.assert_true(ResourceLoader.exists(click_path), "Click sound file exists at path")


func test_audio_player_initialization():
	TestHelperClass.print_step("Audio Player Initialization")

	# Test music player initialization
	TestHelperClass.assert_not_null(audio_manager.music_player, "Music player is initialized")
	TestHelperClass.assert_true(
		audio_manager.music_player is AudioStreamPlayer, "Music player is AudioStreamPlayer type"
	)
	TestHelperClass.assert_true(
		audio_manager.music_player.get_parent() == audio_manager,
		"Music player is child of AudioManager"
	)

	# Test UI click player initialization
	TestHelperClass.assert_not_null(audio_manager.ui_click_player, "UI click player is initialized")
	TestHelperClass.assert_true(
		audio_manager.ui_click_player is AudioStreamPlayer,
		"UI click player is AudioStreamPlayer type"
	)
	TestHelperClass.assert_true(
		audio_manager.ui_click_player.get_parent() == audio_manager,
		"UI click player is child of AudioManager"
	)

	# Test audio bus assignment
	TestHelperClass.assert_equal(
		"Music", audio_manager.music_player.bus, "Music player assigned to Music bus"
	)
	TestHelperClass.assert_equal(
		"SFX", audio_manager.ui_click_player.bus, "UI click player assigned to SFX bus"
	)


func test_stream_loading_and_validation():
	TestHelperClass.print_step("Stream Loading and Validation")

	# Test music stream loading
	TestHelperClass.assert_not_null(audio_manager.music_player.stream, "Music stream is loaded")
	if audio_manager.music_player.stream:
		TestHelperClass.assert_true(
			audio_manager.music_player.stream is AudioStream, "Music stream is AudioStream type"
		)

	# Test click stream loading
	TestHelperClass.assert_not_null(audio_manager.click_stream, "Click stream is loaded")
	if audio_manager.click_stream:
		TestHelperClass.assert_true(
			audio_manager.click_stream is AudioStream, "Click stream is AudioStream type"
		)

	# Test stream resource loading directly
	var loaded_music = load(audio_manager.MUSIC_PATH)
	TestHelperClass.assert_not_null(loaded_music, "Music resource loads successfully")
	TestHelperClass.assert_true(loaded_music is AudioStream, "Loaded music is AudioStream type")

	var loaded_click = load(audio_manager.UI_CLICK_SOUND_PATH)
	TestHelperClass.assert_not_null(loaded_click, "Click resource loads successfully")
	TestHelperClass.assert_true(loaded_click is AudioStream, "Loaded click sound is AudioStream type")


func test_audio_bus_configuration():
	TestHelperClass.print_step("Audio Bus Configuration")

	# Test that required audio buses exist
	var music_bus_index = AudioServer.get_bus_index("Music")
	var sfx_bus_index = AudioServer.get_bus_index("SFX")

	TestHelperClass.assert_true(music_bus_index >= 0, "Music audio bus exists")
	TestHelperClass.assert_true(sfx_bus_index >= 0, "SFX audio bus exists")

	# Test player bus assignments match actual AudioServer buses
	if music_bus_index >= 0:
		TestHelperClass.assert_equal(
			"Music", audio_manager.music_player.bus, "Music player correctly assigned to Music bus"
		)

	if sfx_bus_index >= 0:
		TestHelperClass.assert_equal(
			"SFX",
			audio_manager.ui_click_player.bus,
			"UI click player correctly assigned to SFX bus"
		)


func test_volume_management():
	TestHelperClass.print_step("Volume Management")

	# Store original volume
	var settings_manager = root.get_node("SettingsManager")
	var original_volume = settings_manager.get_setting("music_volume")
	var _was_playing = audio_manager.music_player.playing

	# Test volume update to valid range
	audio_manager.update_music_volume(0.5)
	TestHelperClass.assert_float_equal(
		linear_to_db(0.5), audio_manager.music_player.volume_db, 0.001, "Music volume set correctly"
	)

	# Test volume update to zero (should stop music)
	audio_manager.update_music_volume(0.0)
	TestHelperClass.assert_equal(
		linear_to_db(0.0), audio_manager.music_player.volume_db, "Zero volume set correctly"
	)
	# Note: We don't test playing state as it depends on initialization conditions

	# Test volume update to maximum
	audio_manager.update_music_volume(1.0)
	TestHelperClass.assert_equal(
		linear_to_db(1.0), audio_manager.music_player.volume_db, "Maximum volume set correctly"
	)

	# Test volume range validation
	var test_volumes = [0.0, 0.25, 0.5, 0.75, 1.0]
	for volume in test_volumes:
		audio_manager.update_music_volume(volume)
		var expected_db = linear_to_db(volume)
		TestHelperClass.assert_float_equal(
			expected_db,
			audio_manager.music_player.volume_db,
			0.001,
			"Volume %f converts correctly to dB" % volume
		)

	# Restore original volume
	audio_manager.update_music_volume(original_volume)


func test_music_playback_control():
	TestHelperClass.print_step("Music Playback Control")

	# Test that music player exists and has a stream
	TestHelperClass.assert_not_null(
		audio_manager.music_player, "Music player exists for playback testing"
	)
	TestHelperClass.assert_not_null(
		audio_manager.music_player.stream, "Music player has stream for playback testing"
	)

	# Test playback state management
	# Note: We test the control methods exist and can be called safely
	var _original_playing = audio_manager.music_player.playing

	# Test that playback methods can be called without errors
	if audio_manager.has_method("_start_music"):
		# Method exists but is private - test that the logic is sound
		TestHelperClass.assert_true(true, "Private _start_music method exists")

	if audio_manager.has_method("_stop_music"):
		# Method exists but is private - test that the logic is sound
		TestHelperClass.assert_true(true, "Private _stop_music method exists")

	# Test volume-based playback control
	var settings_manager = root.get_node("SettingsManager")
	var current_volume = settings_manager.get_setting("music_volume")
	if current_volume > 0.0:
		audio_manager.update_music_volume(current_volume)
		TestHelperClass.assert_true(true, "Volume-based playback start works")
	else:
		audio_manager.update_music_volume(0.0)
		TestHelperClass.assert_true(true, "Volume-based playback stop works")


func test_ui_sound_effects():
	TestHelperClass.print_step("UI Sound Effects")

	# Test UI click functionality
	TestHelperClass.assert_not_null(audio_manager.ui_click_player, "UI click player exists")
	TestHelperClass.assert_not_null(audio_manager.click_stream, "Click stream is loaded")

	# Test that play_ui_click can be called safely
	var _original_stream = audio_manager.ui_click_player.stream
	audio_manager.play_ui_click()

	# Verify click stream was assigned to player
	TestHelperClass.assert_equal(
		audio_manager.click_stream,
		audio_manager.ui_click_player.stream,
		"Click stream assigned to player"
	)

	# Test multiple rapid clicks (should not cause errors)
	for i in range(3):
		audio_manager.play_ui_click()
		TestHelperClass.assert_true(true, "Rapid click %d handled safely" % (i + 1))

	# Test click with null stream
	var backup_stream = audio_manager.click_stream
	audio_manager.click_stream = null
	audio_manager.play_ui_click()  # Should not crash
	TestHelperClass.assert_true(true, "Null click stream handled safely")
	audio_manager.click_stream = backup_stream


func test_stream_loop_configuration():
	TestHelperClass.print_step("Stream Loop Configuration")

	# Test that music stream has loop configuration
	var music_stream = audio_manager.music_player.stream
	if music_stream:
		if music_stream is AudioStreamWAV:
			# For WAV files, check loop mode
			var has_loop_mode = "loop_mode" in music_stream
			TestHelperClass.assert_true(has_loop_mode, "WAV stream has loop_mode property")
			if has_loop_mode:
				TestHelperClass.assert_equal(
					AudioStreamWAV.LOOP_FORWARD,
					music_stream.loop_mode,
					"WAV stream set to forward loop"
				)
		elif music_stream is AudioStreamOggVorbis:
			# For OGG files, check loop property
			var has_loop = "loop" in music_stream
			TestHelperClass.assert_true(has_loop, "OGG stream has loop property")
			if has_loop:
				TestHelperClass.assert_true(music_stream.loop, "OGG stream loop enabled")

	# Test loop configuration for different stream types
	TestHelperClass.assert_true(true, "Stream loop configuration tested based on type")


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

	# Test graceful handling of missing resources
	# We can't actually break the resources in tests, but we can verify error handling patterns

	# Test that AudioManager initializes even with potential issues
	TestHelperClass.assert_not_null(
		audio_manager, "AudioManager initializes despite potential resource issues"
	)

	# Test that players are still created even if streams fail to load
	TestHelperClass.assert_not_null(
		audio_manager.music_player, "Music player created regardless of stream loading"
	)
	TestHelperClass.assert_not_null(
		audio_manager.ui_click_player, "UI click player created regardless of stream loading"
	)

	# Test null stream handling in play_ui_click
	var original_click_stream = audio_manager.click_stream
	audio_manager.click_stream = null

	# This should not crash
	audio_manager.play_ui_click()
	TestHelperClass.assert_true(true, "play_ui_click handles null stream gracefully")

	# Restore original stream
	audio_manager.click_stream = original_click_stream

	# Test volume edge cases
	audio_manager.update_music_volume(0.0)
	TestHelperClass.assert_true(true, "Zero volume handled safely")

	audio_manager.update_music_volume(1.0)
	TestHelperClass.assert_true(true, "Maximum volume handled safely")


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

	# Restore original volume settings
	var settings_manager = root.get_node("SettingsManager")
	settings_manager.set_setting("music_volume", original_music_volume)
	settings_manager.set_setting("sfx_volume", original_sfx_volume)

	# Update AudioManager to original settings
	audio_manager.update_music_volume(original_music_volume)

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