extends SceneTree

## Test suite for Tile component
##
## Tests tile initialization, texture management, and gem types.

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

var tile_scene: PackedScene
var tile_instance: Node2D
var test_viewport: SubViewport


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("Tile Component")

	# Setup test environment
	setup_test_environment()

	# Run test suites
	test_basic_functionality()
	test_tile_constants()
	test_texture_management()
	test_gem_type_management()
	test_visual_feedback_system()
	test_state_management()
	test_input_validation()
	test_scaling_and_sizing()
	test_memory_safety()
	test_error_handling()

	# Cleanup
	cleanup_tests()

	TestHelperClass.print_test_footer("Tile Component")


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

	# Load Tile scene
	tile_scene = load("res://scenes/game/gameplays/tile.tscn")
	TestHelperClass.assert_not_null(tile_scene, "Tile scene loads successfully")

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

	# Instance Tile in test viewport
	if tile_scene:
		tile_instance = tile_scene.instantiate()
		test_viewport.add_child(tile_instance)
		TestHelperClass.assert_not_null(tile_instance, "Tile instance created successfully")

		# Wait for initialization
		await process_frame
		await process_frame


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

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

	# Test that Tile has expected properties
	var expected_properties = [
		"tile_type",
		"grid_position",
		"is_selected",
		"is_highlighted",
		"original_scale",
		"active_gem_types"
	]
	for prop in expected_properties:
		TestHelperClass.assert_true(prop in tile_instance, "Tile has property: " + prop)

	# Test that Tile has expected methods
	var expected_methods = [
		"set_active_gem_types",
		"get_active_gem_count",
		"add_gem_type",
		"remove_gem_type",
		"force_reset_visual_state"
	]
	TestHelperClass.assert_has_methods(tile_instance, expected_methods, "Tile component methods")

	# Test signals
	TestHelperClass.assert_true(
		tile_instance.has_signal("tile_selected"), "Tile has tile_selected signal"
	)

	# Test sprite reference
	TestHelperClass.assert_not_null(tile_instance.sprite, "Sprite node is available")
	TestHelperClass.assert_true(tile_instance.sprite is Sprite2D, "Sprite is Sprite2D type")

	# Test group membership
	TestHelperClass.assert_true(tile_instance.is_in_group("tiles"), "Tile is in 'tiles' group")


func test_tile_constants():
	TestHelperClass.print_step("Tile Constants")

	if not tile_instance:
		return

	# Test TILE_SIZE constant
	TestHelperClass.assert_equal(48, tile_instance.TILE_SIZE, "TILE_SIZE constant is correct")

	# Test all_gem_textures array
	TestHelperClass.assert_not_null(tile_instance.all_gem_textures, "All gem textures array exists")
	TestHelperClass.assert_true(
		tile_instance.all_gem_textures is Array, "All gem textures is Array type"
	)
	TestHelperClass.assert_equal(
		8, tile_instance.all_gem_textures.size(), "All gem textures has expected count"
	)

	# Test that all gem textures are valid
	for i in range(tile_instance.all_gem_textures.size()):
		var texture = tile_instance.all_gem_textures[i]
		TestHelperClass.assert_not_null(texture, "Gem texture %d is not null" % i)
		TestHelperClass.assert_true(texture is Texture2D, "Gem texture %d is Texture2D type" % i)


func test_texture_management():
	TestHelperClass.print_step("Texture Management")

	if not tile_instance:
		return

	# Test default gem types initialization
	TestHelperClass.assert_not_null(tile_instance.active_gem_types, "Active gem types is initialized")
	TestHelperClass.assert_true(
		tile_instance.active_gem_types is Array, "Active gem types is Array type"
	)
	TestHelperClass.assert_true(
		tile_instance.active_gem_types.size() > 0, "Active gem types has content"
	)

	# Test texture assignment for valid tile types
	var original_type = tile_instance.tile_type

	for i in range(min(3, tile_instance.active_gem_types.size())):
		tile_instance.tile_type = i
		TestHelperClass.assert_equal(i, tile_instance.tile_type, "Tile type set correctly to %d" % i)

		if tile_instance.sprite:
			TestHelperClass.assert_not_null(
				tile_instance.sprite.texture, "Sprite texture assigned for type %d" % i
			)

	# Restore original type
	tile_instance.tile_type = original_type


func test_gem_type_management():
	TestHelperClass.print_step("Gem Type Management")

	if not tile_instance:
		return

	# Store original state
	var original_gem_types = tile_instance.active_gem_types.duplicate()

	# Test set_active_gem_types with valid array
	var test_gems = [0, 1, 2]
	tile_instance.set_active_gem_types(test_gems)
	TestHelperClass.assert_equal(
		3, tile_instance.get_active_gem_count(), "Active gem count set correctly"
	)

	# Test add_gem_type
	var add_result = tile_instance.add_gem_type(3)
	TestHelperClass.assert_true(add_result, "Valid gem type added successfully")
	TestHelperClass.assert_equal(
		4, tile_instance.get_active_gem_count(), "Gem count increased after addition"
	)

	# Test adding duplicate gem type
	var duplicate_result = tile_instance.add_gem_type(3)
	TestHelperClass.assert_false(duplicate_result, "Duplicate gem type addition rejected")
	TestHelperClass.assert_equal(
		4, tile_instance.get_active_gem_count(), "Gem count unchanged after duplicate"
	)

	# Test add_gem_type with invalid index
	var invalid_add = tile_instance.add_gem_type(99)
	TestHelperClass.assert_false(invalid_add, "Invalid gem index addition rejected")

	# Test remove_gem_type
	var remove_result = tile_instance.remove_gem_type(3)
	TestHelperClass.assert_true(remove_result, "Valid gem type removed successfully")
	TestHelperClass.assert_equal(
		3, tile_instance.get_active_gem_count(), "Gem count decreased after removal"
	)

	# Test removing non-existent gem type
	var nonexistent_remove = tile_instance.remove_gem_type(99)
	TestHelperClass.assert_false(nonexistent_remove, "Non-existent gem type removal rejected")

	# Test minimum gem types protection
	tile_instance.set_active_gem_types([0, 1])  # Set to minimum
	var protected_remove = tile_instance.remove_gem_type(0)
	TestHelperClass.assert_false(protected_remove, "Minimum gem types protection active")
	TestHelperClass.assert_equal(2, tile_instance.get_active_gem_count(), "Minimum gem count preserved")

	# Restore original state
	tile_instance.set_active_gem_types(original_gem_types)


func test_visual_feedback_system():
	TestHelperClass.print_step("Visual Feedback System")

	if not tile_instance:
		return

	# Store original state
	var original_selected = tile_instance.is_selected
	var original_highlighted = tile_instance.is_highlighted

	# Test selection visual feedback
	tile_instance.is_selected = true
	TestHelperClass.assert_true(tile_instance.is_selected, "Selection state set correctly")

	# Wait for potential animation
	await process_frame

	if tile_instance.sprite:
		# Test that modulate is brighter when selected
		var modulate = tile_instance.sprite.modulate
		TestHelperClass.assert_true(
			modulate.r > 1.0 or modulate.g > 1.0 or modulate.b > 1.0,
			"Selected tile has brighter modulate"
		)

	# Test highlight visual feedback
	tile_instance.is_selected = false
	tile_instance.is_highlighted = true
	TestHelperClass.assert_true(tile_instance.is_highlighted, "Highlight state set correctly")

	# Wait for potential animation
	await process_frame

	# Test normal state
	tile_instance.is_highlighted = false
	TestHelperClass.assert_false(tile_instance.is_highlighted, "Normal state restored")

	# Test force reset
	tile_instance.is_selected = true
	tile_instance.is_highlighted = true
	tile_instance.force_reset_visual_state()
	TestHelperClass.assert_false(tile_instance.is_selected, "Force reset clears selection")
	TestHelperClass.assert_false(tile_instance.is_highlighted, "Force reset clears highlight")

	# Restore original state
	tile_instance.is_selected = original_selected
	tile_instance.is_highlighted = original_highlighted


func test_state_management():
	TestHelperClass.print_step("State Management")

	if not tile_instance:
		return

	# Test initial state
	TestHelperClass.assert_true(tile_instance.tile_type >= 0, "Initial tile type is non-negative")
	TestHelperClass.assert_true(
		tile_instance.grid_position is Vector2i, "Grid position is Vector2i type"
	)
	TestHelperClass.assert_true(
		tile_instance.original_scale is Vector2, "Original scale is Vector2 type"
	)

	# Test tile type bounds checking
	var original_type = tile_instance.tile_type
	var max_valid_type = tile_instance.active_gem_types.size() - 1

	# Test valid tile type
	if max_valid_type >= 0:
		tile_instance.tile_type = max_valid_type
		TestHelperClass.assert_equal(max_valid_type, tile_instance.tile_type, "Valid tile type accepted")

	# Test state consistency
	TestHelperClass.assert_true(
		tile_instance.tile_type < tile_instance.active_gem_types.size(),
		"Tile type within active gems range"
	)

	# Restore original type
	tile_instance.tile_type = original_type


func test_input_validation():
	TestHelperClass.print_step("Input Validation")

	if not tile_instance:
		return

	# Test empty gem types array
	var original_gems = tile_instance.active_gem_types.duplicate()
	tile_instance.set_active_gem_types([])
	# Should fall back to defaults or maintain previous state
	TestHelperClass.assert_true(
		tile_instance.get_active_gem_count() > 0, "Empty gem array handled gracefully"
	)

	# Test null gem types array
	tile_instance.set_active_gem_types(null)
	TestHelperClass.assert_true(
		tile_instance.get_active_gem_count() > 0, "Null gem array handled gracefully"
	)

	# Test invalid gem indices in array
	tile_instance.set_active_gem_types([0, 1, 99, 2])  # 99 is invalid
	# Should use fallback or filter invalid indices
	TestHelperClass.assert_true(
		tile_instance.get_active_gem_count() > 0, "Invalid gem indices handled gracefully"
	)

	# Test negative gem indices
	var negative_add = tile_instance.add_gem_type(-1)
	TestHelperClass.assert_false(negative_add, "Negative gem index rejected")

	# Test out-of-bounds gem indices
	var oob_add = tile_instance.add_gem_type(tile_instance.all_gem_textures.size())
	TestHelperClass.assert_false(oob_add, "Out-of-bounds gem index rejected")

	# Restore original state
	tile_instance.set_active_gem_types(original_gems)


func test_scaling_and_sizing():
	TestHelperClass.print_step("Scaling and Sizing")

	if not tile_instance:
		return

	# Test original scale calculation
	TestHelperClass.assert_not_null(tile_instance.original_scale, "Original scale is calculated")
	TestHelperClass.assert_true(tile_instance.original_scale.x > 0, "Original scale X is positive")
	TestHelperClass.assert_true(tile_instance.original_scale.y > 0, "Original scale Y is positive")

	# Test that tile size is respected
	if tile_instance.sprite and tile_instance.sprite.texture:
		var texture_size = tile_instance.sprite.texture.get_size()
		var scaled_size = texture_size * tile_instance.original_scale
		var max_dimension = max(scaled_size.x, scaled_size.y)
		TestHelperClass.assert_true(
			max_dimension <= tile_instance.TILE_SIZE + 1, "Scaled tile fits within TILE_SIZE"
		)

	# Test scale animation for visual feedback
	var original_scale = tile_instance.sprite.scale if tile_instance.sprite else Vector2.ONE

	# Test selection scaling
	tile_instance.is_selected = true
	await process_frame
	await process_frame  # Wait for animation

	if tile_instance.sprite:
		var selected_scale = tile_instance.sprite.scale
		TestHelperClass.assert_true(
			selected_scale.x >= original_scale.x, "Selected tile scale is larger or equal"
		)

	# Reset to normal
	tile_instance.is_selected = false
	await process_frame
	await process_frame


func test_memory_safety():
	TestHelperClass.print_step("Memory Safety")

	if not tile_instance:
		return

	# Test sprite null checking
	var original_sprite = tile_instance.sprite
	tile_instance.sprite = null

	# These operations should not crash
	tile_instance._set_tile_type(0)
	tile_instance._update_visual_feedback()
	tile_instance.force_reset_visual_state()

	TestHelperClass.assert_true(true, "Null sprite operations handled safely")

	# Restore sprite
	tile_instance.sprite = original_sprite

	# Test valid instance checking in visual updates
	if tile_instance.sprite:
		TestHelperClass.assert_true(is_instance_valid(tile_instance.sprite), "Sprite instance is valid")

	# Test gem types array integrity
	TestHelperClass.assert_true(
		tile_instance.active_gem_types is Array, "Active gem types maintains Array type"
	)

	# Test that gem indices are within bounds
	for gem_index in tile_instance.active_gem_types:
		TestHelperClass.assert_true(gem_index >= 0, "Gem index is non-negative")
		TestHelperClass.assert_true(
			gem_index < tile_instance.all_gem_textures.size(),
			"Gem index within texture array bounds"
		)


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

	if not tile_instance:
		return

	# Test graceful handling of missing sprite during initialization
	var backup_sprite = tile_instance.sprite
	tile_instance.sprite = null

	# Test that _set_tile_type handles null sprite gracefully
	tile_instance._set_tile_type(0)
	TestHelperClass.assert_true(true, "Tile type setting handles null sprite gracefully")

	# Test that scaling handles null sprite gracefully
	tile_instance._scale_sprite_to_fit()
	TestHelperClass.assert_true(true, "Sprite scaling handles null sprite gracefully")

	# Restore sprite
	tile_instance.sprite = backup_sprite

	# Test invalid tile type handling
	var original_type = tile_instance.tile_type
	tile_instance._set_tile_type(-1)  # Invalid negative type
	tile_instance._set_tile_type(999)  # Invalid large type

	# Should not crash and should maintain reasonable state
	TestHelperClass.assert_true(true, "Invalid tile types handled gracefully")

	# Restore original type
	tile_instance.tile_type = original_type

	# Test array bounds protection
	var large_gem_types = []
	for i in range(50):  # Create array larger than texture array
		large_gem_types.append(i)

	tile_instance.set_active_gem_types(large_gem_types)
	# Should fall back to safe defaults
	TestHelperClass.assert_true(
		tile_instance.get_active_gem_count() <= tile_instance.all_gem_textures.size(),
		"Large gem array handled safely"
	)


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

	# Clean up tile instance
	if tile_instance and is_instance_valid(tile_instance):
		tile_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")
