# Scene operations module
# Handles: create_scene, add_node, save_scene, load_sprite, list_scenes, get_scene_tree, get_node_properties
#          set_node_property, delete_node, instance_scene, rename_node, move_node, duplicate_node, add_to_group
# Corresponds to: test_basics.py, test_e2e_basics.py, test_e2e_scenes.py, test_scene_query.py, test_scene_manipulation.py
class_name SceneOps


## Create a new scene with a specified root node type
static func create_scene(ctx: SceneTree, params: Dictionary) -> void:
	print("Creating scene: " + params.scene_path)
	
	# Get project paths and log them for debugging
	var project_res_path: String = "res://"
	var project_user_path: String = "user://"
	var global_res_path: String = ProjectSettings.globalize_path(project_res_path)
	var global_user_path: String = ProjectSettings.globalize_path(project_user_path)
	
	if ctx.debug_mode:
		print("Project paths:")
		print("- res:// path: " + project_res_path)
		print("- user:// path: " + project_user_path)
		print("- Globalized res:// path: " + global_res_path)
		print("- Globalized user:// path: " + global_user_path)
		
		# Print some common environment variables for debugging
		print("Environment variables:")
		var env_vars: Array = ["PATH", "HOME", "USER", "TEMP", "GODOT_PATH"]
		for env_var in env_vars:
			if OS.has_environment(env_var):
				print("  " + env_var + " = " + OS.get_environment(env_var))
	
	# Normalize the scene path
	var full_scene_path: String = params.scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	if ctx.debug_mode:
		print("Scene path (with res://): " + full_scene_path)
	
	# Convert resource path to an absolute path
	var absolute_scene_path: String = ProjectSettings.globalize_path(full_scene_path)
	if ctx.debug_mode:
		print("Absolute scene path: " + absolute_scene_path)
	
	# Get the scene directory paths
	var scene_dir_res: String = full_scene_path.get_base_dir()
	var scene_dir_abs: String = absolute_scene_path.get_base_dir()
	if ctx.debug_mode:
		print("Scene directory (resource path): " + scene_dir_res)
		print("Scene directory (absolute path): " + scene_dir_abs)
	
	# Only do extensive testing in debug mode
	if ctx.debug_mode:
		# Try to create a simple test file in the project root to verify write access
		var initial_test_file_path: String = "res://godot_mcp_test_write.tmp"
		var initial_test_file: FileAccess = FileAccess.open(initial_test_file_path, FileAccess.WRITE)
		if initial_test_file:
			initial_test_file.store_string("Test write access")
			initial_test_file.close()
			print("Successfully wrote test file to project root: " + initial_test_file_path)
			
			# Verify the test file exists
			var initial_test_file_exists: bool = FileAccess.file_exists(initial_test_file_path)
			print("Test file exists check: " + str(initial_test_file_exists))
			
			# Clean up the test file
			if initial_test_file_exists:
				var remove_error: int = DirAccess.remove_absolute(ProjectSettings.globalize_path(initial_test_file_path))
				print("Test file removal result: " + str(remove_error))
		else:
			var write_error: int = FileAccess.get_open_error()
			printerr("Failed to write test file to project root: " + str(write_error))
			printerr("This indicates a serious permission issue with the project directory")
	
	# Use traditional if-else statement for better compatibility
	var root_node_type: String = "Node2D"  # Default value
	if params.has("root_node_type"):
		root_node_type = params.root_node_type
	if ctx.debug_mode:
		print("Root node type: " + root_node_type)
	
	# Create the root node
	var scene_root: Node = ctx.instantiate_class(root_node_type)
	if not scene_root:
		printerr("Failed to instantiate node of type: " + root_node_type)
		printerr("Make sure the class exists and can be instantiated")
		printerr("Check if the class is registered in ClassDB or available as a script")
		ctx.quit(1)
	
	scene_root.name = "root"
	if ctx.debug_mode:
		print("Root node created with name: " + scene_root.name)
	
	# Set the owner of the root node to itself (important for scene saving)
	scene_root.owner = scene_root
	
	# Pack the scene
	var packed_scene: PackedScene = PackedScene.new()
	var result: int = packed_scene.pack(scene_root)
	if ctx.debug_mode:
		print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
	
	if result == OK:
		# Only do extensive testing in debug mode
		if ctx.debug_mode:
			# First, let's verify we can write to the project directory
			print("Testing write access to project directory...")
			var test_write_path: String = "res://test_write_access.tmp"
			var test_write_abs: String = ProjectSettings.globalize_path(test_write_path)
			var test_file: FileAccess = FileAccess.open(test_write_path, FileAccess.WRITE)
			
			if test_file:
				test_file.store_string("Write test")
				test_file.close()
				print("Successfully wrote test file to project directory")
				
				# Clean up test file
				if FileAccess.file_exists(test_write_path):
					var remove_error: int = DirAccess.remove_absolute(test_write_abs)
					print("Test file removal result: " + str(remove_error))
			else:
				var write_error: int = FileAccess.get_open_error()
				printerr("Failed to write test file to project directory: " + str(write_error))
				printerr("This may indicate permission issues with the project directory")
				# Continue anyway, as the scene directory might still be writable
		
		# Ensure the scene directory exists using DirAccess
		if ctx.debug_mode:
			print("Ensuring scene directory exists...")
		
		# Get the scene directory relative to res://
		var scene_dir_relative: String = scene_dir_res.substr(6)  # Remove "res://" prefix
		if ctx.debug_mode:
			print("Scene directory (relative to res://): " + scene_dir_relative)
		
		# Create the directory if needed
		if not scene_dir_relative.is_empty():
			# First check if it exists
			var dir_exists: bool = DirAccess.dir_exists_absolute(scene_dir_abs)
			if ctx.debug_mode:
				print("Directory exists check (absolute): " + str(dir_exists))
			
			if not dir_exists:
				if ctx.debug_mode:
					print("Directory doesn't exist, creating: " + scene_dir_relative)
				
				# Try to create the directory using DirAccess
				var dir: DirAccess = DirAccess.open("res://")
				if dir == null:
					var open_error: int = DirAccess.get_open_error()
					printerr("Failed to open res:// directory: " + str(open_error))
					
					# Try alternative approach with absolute path
					if ctx.debug_mode:
						print("Trying alternative directory creation approach...")
					var make_dir_error: int = DirAccess.make_dir_recursive_absolute(scene_dir_abs)
					if ctx.debug_mode:
						print("Make directory result (absolute): " + str(make_dir_error))
					
					if make_dir_error != OK:
						printerr("Failed to create directory using absolute path")
						printerr("Error code: " + str(make_dir_error))
						ctx.quit(1)
				else:
					# Create the directory using the DirAccess instance
					if ctx.debug_mode:
						print("Creating directory using DirAccess: " + scene_dir_relative)
					var make_dir_error: int = dir.make_dir_recursive(scene_dir_relative)
					if ctx.debug_mode:
						print("Make directory result: " + str(make_dir_error))
					
					if make_dir_error != OK:
						printerr("Failed to create directory: " + scene_dir_relative)
						printerr("Error code: " + str(make_dir_error))
						ctx.quit(1)
				
				# Verify the directory was created
				dir_exists = DirAccess.dir_exists_absolute(scene_dir_abs)
				if ctx.debug_mode:
					print("Directory exists check after creation: " + str(dir_exists))
				
				if not dir_exists:
					printerr("Directory reported as created but does not exist: " + scene_dir_abs)
					printerr("This may indicate a problem with path resolution or permissions")
					ctx.quit(1)
			elif ctx.debug_mode:
				print("Directory already exists: " + scene_dir_abs)
		
		# Save the scene
		if ctx.debug_mode:
			print("Saving scene to: " + full_scene_path)
		var save_error: int = ResourceSaver.save(packed_scene, full_scene_path)
		if ctx.debug_mode:
			print("Save result: " + str(save_error) + " (OK=" + str(OK) + ")")
		
		if save_error == OK:
			# Only do extensive testing in debug mode
			if ctx.debug_mode:
				# Wait a moment to ensure file system has time to complete the write
				print("Waiting for file system to complete write operation...")
				OS.delay_msec(500)  # 500ms delay
				
				# Verify the file was actually created using multiple methods
				var file_check_abs: bool = FileAccess.file_exists(absolute_scene_path)
				print("File exists check (absolute path): " + str(file_check_abs))
				
				var file_check_res: bool = FileAccess.file_exists(full_scene_path)
				print("File exists check (resource path): " + str(file_check_res))
				
				var res_exists: bool = ResourceLoader.exists(full_scene_path)
				print("Resource exists check: " + str(res_exists))
				
				# If file doesn't exist by absolute path, try to create a test file in the same directory
				if not file_check_abs and not file_check_res:
					printerr("Scene file not found after save. Trying to diagnose the issue...")
					
					# Try to write a test file to the same directory
					var test_scene_file_path: String = scene_dir_res + "/test_scene_file.tmp"
					var test_scene_file: FileAccess = FileAccess.open(test_scene_file_path, FileAccess.WRITE)
					
					if test_scene_file:
						test_scene_file.store_string("Test scene directory write")
						test_scene_file.close()
						print("Successfully wrote test file to scene directory: " + test_scene_file_path)
						
						# Check if the test file exists
						var test_file_exists: bool = FileAccess.file_exists(test_scene_file_path)
						print("Test file exists: " + str(test_file_exists))
						
						if test_file_exists:
							# Directory is writable, so the issue is with scene saving
							printerr("Directory is writable but scene file wasn't created.")
							printerr("This suggests an issue with ResourceSaver.save() or the packed scene.")
							
							# Try saving with a different approach
							print("Trying alternative save approach...")
							var alt_save_error: int = ResourceSaver.save(packed_scene, test_scene_file_path + ".tscn")
							print("Alternative save result: " + str(alt_save_error))
							
							# Clean up test files
							DirAccess.remove_absolute(ProjectSettings.globalize_path(test_scene_file_path))
							if alt_save_error == OK:
								DirAccess.remove_absolute(ProjectSettings.globalize_path(test_scene_file_path + ".tscn"))
						else:
							printerr("Test file couldn't be verified. This suggests filesystem access issues.")
					else:
						var write_error: int = FileAccess.get_open_error()
						printerr("Failed to write test file to scene directory: " + str(write_error))
						printerr("This confirms there are permission or path issues with the scene directory.")
					
					# Return error since we couldn't create the scene file
					printerr("Failed to create scene: " + params.scene_path)
					ctx.quit(1)
				
				# If we get here, at least one of our file checks passed
				if file_check_abs or file_check_res or res_exists:
					print("Scene file verified to exist!")
					
					# Try to load the scene to verify it's valid
					var test_load: Resource = ResourceLoader.load(full_scene_path)
					if test_load:
						print("Scene created and verified successfully at: " + params.scene_path)
						print("Scene file can be loaded correctly.")
					else:
						print("Scene file exists but cannot be loaded. It may be corrupted or incomplete.")
						# Continue anyway since the file exists
					
					print("Scene created successfully at: " + params.scene_path)
				else:
					printerr("All file existence checks failed despite successful save operation.")
					printerr("This indicates a serious issue with file system access or path resolution.")
					ctx.quit(1)
			else:
				# In non-debug mode, just check if the file exists
				var file_exists: bool = FileAccess.file_exists(full_scene_path)
				if file_exists:
					print("Scene created successfully at: " + params.scene_path)
				else:
					printerr("Failed to create scene: " + params.scene_path)
					ctx.quit(1)
		else:
			# Handle specific error codes
			var error_message: String = "Failed to save scene. Error code: " + str(save_error)
			
			if save_error == ERR_CANT_CREATE:
				error_message += " (ERR_CANT_CREATE - Cannot create the scene file)"
			elif save_error == ERR_CANT_OPEN:
				error_message += " (ERR_CANT_OPEN - Cannot open the scene file for writing)"
			elif save_error == ERR_FILE_CANT_WRITE:
				error_message += " (ERR_FILE_CANT_WRITE - Cannot write to the scene file)"
			elif save_error == ERR_FILE_NO_PERMISSION:
				error_message += " (ERR_FILE_NO_PERMISSION - No permission to write the scene file)"
			
			printerr(error_message)
			ctx.quit(1)
	else:
		printerr("Failed to pack scene: " + str(result))
		printerr("Error code: " + str(result))
		ctx.quit(1)


## Add a node to an existing scene
static func add_node(ctx: SceneTree, params: Dictionary) -> void:
	print("Adding node to scene: " + params.scene_path)
	
	var full_scene_path: String = params.scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	if ctx.debug_mode:
		print("Scene path (with res://): " + full_scene_path)
	
	var absolute_scene_path: String = ProjectSettings.globalize_path(full_scene_path)
	if ctx.debug_mode:
		print("Absolute scene path: " + absolute_scene_path)
	
	if not FileAccess.file_exists(absolute_scene_path):
		printerr("Scene file does not exist at: " + absolute_scene_path)
		ctx.quit(1)
	
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("Scene loaded successfully")
	var scene_root: Node = scene.instantiate()
	if ctx.debug_mode:
		print("Scene instantiated")
	
	# Use traditional if-else statement for better compatibility
	var parent_path: String = "root"  # Default value
	if params.has("parent_node_path"):
		parent_path = params.parent_node_path
	if ctx.debug_mode:
		print("Parent path: " + parent_path)
	
	var parent: Node = scene_root
	if parent_path != "root":
		parent = scene_root.get_node(parent_path.replace("root/", ""))
		if not parent:
			printerr("Parent node not found: " + parent_path)
			ctx.quit(1)
	if ctx.debug_mode:
		print("Parent node found: " + parent.name)
	
	if ctx.debug_mode:
		print("Instantiating node of type: " + params.node_type)
	var new_node: Node = ctx.instantiate_class(params.node_type)
	if not new_node:
		printerr("Failed to instantiate node of type: " + params.node_type)
		printerr("Make sure the class exists and can be instantiated")
		printerr("Check if the class is registered in ClassDB or available as a script")
		ctx.quit(1)
	new_node.name = params.node_name
	if ctx.debug_mode:
		print("New node created with name: " + new_node.name)
	
	if params.has("properties"):
		if ctx.debug_mode:
			print("Setting properties on node")
		var properties: Dictionary = params.properties
		for property in properties:
			if ctx.debug_mode:
				print("Setting property: " + property + " = " + str(properties[property]))
			new_node.set(property, properties[property])
	
	parent.add_child(new_node)
	new_node.owner = scene_root
	if ctx.debug_mode:
		print("Node added to parent and ownership set")
	
	var packed_scene: PackedScene = PackedScene.new()
	var result: int = packed_scene.pack(scene_root)
	if ctx.debug_mode:
		print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
	
	if result == OK:
		if ctx.debug_mode:
			print("Saving scene to: " + absolute_scene_path)
		var save_error: int = ResourceSaver.save(packed_scene, absolute_scene_path)
		if ctx.debug_mode:
			print("Save result: " + str(save_error) + " (OK=" + str(OK) + ")")
		if save_error == OK:
			if ctx.debug_mode:
				var file_check_after: bool = FileAccess.file_exists(absolute_scene_path)
				print("File exists check after save: " + str(file_check_after))
				if file_check_after:
					print("Node '" + params.node_name + "' of type '" + params.node_type + "' added successfully")
				else:
					printerr("File reported as saved but does not exist at: " + absolute_scene_path)
			else:
				print("Node '" + params.node_name + "' of type '" + params.node_type + "' added successfully")
		else:
			printerr("Failed to save scene: " + str(save_error))
	else:
		printerr("Failed to pack scene: " + str(result))


## Load a sprite into a Sprite2D node
static func load_sprite(ctx: SceneTree, params: Dictionary) -> void:
	print("Loading sprite into scene: " + params.scene_path)
	
	# Ensure the scene path starts with res:// for Godot's resource system
	var full_scene_path: String = params.scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	if ctx.debug_mode:
		print("Full scene path (with res://): " + full_scene_path)
	
	# Check if the scene file exists
	var file_check: bool = FileAccess.file_exists(full_scene_path)
	if ctx.debug_mode:
		print("Scene file exists check: " + str(file_check))
	
	if not file_check:
		printerr("Scene file does not exist at: " + full_scene_path)
		# Get the absolute path for reference
		var absolute_path: String = ProjectSettings.globalize_path(full_scene_path)
		printerr("Absolute file path that doesn't exist: " + absolute_path)
		ctx.quit(1)
	
	# Ensure the texture path starts with res:// for Godot's resource system
	var full_texture_path: String = params.texture_path
	if not full_texture_path.begins_with("res://"):
		full_texture_path = "res://" + full_texture_path
	
	if ctx.debug_mode:
		print("Full texture path (with res://): " + full_texture_path)
	
	# Load the scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("Scene loaded successfully")
	
	# Instance the scene
	var scene_root: Node = scene.instantiate()
	if ctx.debug_mode:
		print("Scene instantiated")
	
	# Find the sprite node
	var node_path: String = params.node_path
	if ctx.debug_mode:
		print("Original node path: " + node_path)
	
	if node_path.begins_with("root/"):
		node_path = node_path.substr(5)  # Remove "root/" prefix
		if ctx.debug_mode:
			print("Node path after removing 'root/' prefix: " + node_path)
	
	var sprite_node: Node = null
	if node_path == "":
		# If no node path, assume root is the sprite
		sprite_node = scene_root
		if ctx.debug_mode:
			print("Using root node as sprite node")
	else:
		sprite_node = scene_root.get_node(node_path)
		if sprite_node and ctx.debug_mode:
			print("Found sprite node: " + sprite_node.name)
	
	if not sprite_node:
		printerr("Node not found: " + params.node_path)
		ctx.quit(1)
	
	# Check if the node is a Sprite2D or compatible type
	if ctx.debug_mode:
		print("Node class: " + sprite_node.get_class())
	if not (sprite_node is Sprite2D or sprite_node is Sprite3D or sprite_node is TextureRect):
		printerr("Node is not a sprite-compatible type: " + sprite_node.get_class())
		ctx.quit(1)
	
	# Load the texture
	if ctx.debug_mode:
		print("Loading texture from: " + full_texture_path)
	var texture: Texture2D = load(full_texture_path)
	if not texture:
		printerr("Failed to load texture: " + full_texture_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("Texture loaded successfully")
	
	# Set the texture on the sprite
	if sprite_node is Sprite2D or sprite_node is Sprite3D:
		sprite_node.texture = texture
		if ctx.debug_mode:
			print("Set texture on Sprite2D/Sprite3D node")
	elif sprite_node is TextureRect:
		sprite_node.texture = texture
		if ctx.debug_mode:
			print("Set texture on TextureRect node")
	
	# Save the modified scene
	var packed_scene: PackedScene = PackedScene.new()
	var result: int = packed_scene.pack(scene_root)
	if ctx.debug_mode:
		print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
	
	if result == OK:
		if ctx.debug_mode:
			print("Saving scene to: " + full_scene_path)
		var error: int = ResourceSaver.save(packed_scene, full_scene_path)
		if ctx.debug_mode:
			print("Save result: " + str(error) + " (OK=" + str(OK) + ")")
		
		if error == OK:
			# Verify the file was actually updated
			if ctx.debug_mode:
				var file_check_after: bool = FileAccess.file_exists(full_scene_path)
				print("File exists check after save: " + str(file_check_after))
				
				if file_check_after:
					print("Sprite loaded successfully with texture: " + full_texture_path)
					# Get the absolute path for reference
					var absolute_path: String = ProjectSettings.globalize_path(full_scene_path)
					print("Absolute file path: " + absolute_path)
				else:
					printerr("File reported as saved but does not exist at: " + full_scene_path)
			else:
				print("Sprite loaded successfully with texture: " + full_texture_path)
		else:
			printerr("Failed to save scene: " + str(error))
	else:
		printerr("Failed to pack scene: " + str(result))


## Save changes to a scene file
static func save_scene(ctx: SceneTree, params: Dictionary) -> void:
	print("Saving scene: " + params.scene_path)
	
	# Ensure the scene path starts with res:// for Godot's resource system
	var full_scene_path: String = params.scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	if ctx.debug_mode:
		print("Full scene path (with res://): " + full_scene_path)
	
	# Check if the scene file exists
	var file_check: bool = FileAccess.file_exists(full_scene_path)
	if ctx.debug_mode:
		print("Scene file exists check: " + str(file_check))
	
	if not file_check:
		printerr("Scene file does not exist at: " + full_scene_path)
		# Get the absolute path for reference
		var absolute_path: String = ProjectSettings.globalize_path(full_scene_path)
		printerr("Absolute file path that doesn't exist: " + absolute_path)
		ctx.quit(1)
	
	# Load the scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("Scene loaded successfully")
	
	# Instance the scene
	var scene_root: Node = scene.instantiate()
	if ctx.debug_mode:
		print("Scene instantiated")
	
	# Determine save path
	var save_path: String = params.new_path if params.has("new_path") else full_scene_path
	if params.has("new_path") and not save_path.begins_with("res://"):
		save_path = "res://" + save_path
	
	if ctx.debug_mode:
		print("Save path: " + save_path)
	
	# Create directory if it doesn't exist
	if params.has("new_path"):
		var dir: DirAccess = DirAccess.open("res://")
		if dir == null:
			printerr("Failed to open res:// directory")
			printerr("DirAccess error: " + str(DirAccess.get_open_error()))
			ctx.quit(1)
			
		var scene_dir: String = save_path.get_base_dir()
		if ctx.debug_mode:
			print("Scene directory: " + scene_dir)
		
		if scene_dir != "res://" and not dir.dir_exists(scene_dir.substr(6)):  # Remove "res://" prefix
			if ctx.debug_mode:
				print("Creating directory: " + scene_dir)
			var error: int = dir.make_dir_recursive(scene_dir.substr(6))  # Remove "res://" prefix
			if error != OK:
				printerr("Failed to create directory: " + scene_dir + ", error: " + str(error))
				ctx.quit(1)
	
	# Create a packed scene
	var packed_scene: PackedScene = PackedScene.new()
	var result: int = packed_scene.pack(scene_root)
	if ctx.debug_mode:
		print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
	
	if result == OK:
		if ctx.debug_mode:
			print("Saving scene to: " + save_path)
		var error: int = ResourceSaver.save(packed_scene, save_path)
		if ctx.debug_mode:
			print("Save result: " + str(error) + " (OK=" + str(OK) + ")")
		
		if error == OK:
			# Verify the file was actually created/updated
			if ctx.debug_mode:
				var file_check_after: bool = FileAccess.file_exists(save_path)
				print("File exists check after save: " + str(file_check_after))
				
				if file_check_after:
					print("Scene saved successfully to: " + save_path)
					# Get the absolute path for reference
					var absolute_path: String = ProjectSettings.globalize_path(save_path)
					print("Absolute file path: " + absolute_path)
				else:
					printerr("File reported as saved but does not exist at: " + save_path)
			else:
				print("Scene saved successfully to: " + save_path)
		else:
			printerr("Failed to save scene: " + str(error))
	else:
		printerr("Failed to pack scene: " + str(result))


# ============================================================================
# Scene Query Operations
# ============================================================================


## List all scene files (.tscn) in the project
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - directory: String - Optional subdirectory to search in (relative to res://)
##   - recursive: bool - Whether to search recursively (default: true)
static func list_scenes_op(ctx: SceneTree, params: Dictionary) -> void:
	var directory: String = params.get("directory", "")
	var recursive: bool = params.get("recursive", true)
	
	if ctx.debug_mode:
		print("[DEBUG] Listing scenes in directory: " + directory)
		print("[DEBUG] Recursive: " + str(recursive))
	
	# Normalize directory path
	var search_path: String = "res://"
	if not directory.is_empty():
		if directory.begins_with("res://"):
			search_path = directory
		else:
			search_path = "res://" + directory
	
	# Ensure path ends with /
	if not search_path.ends_with("/"):
		search_path += "/"
	
	if ctx.debug_mode:
		print("[DEBUG] Search path: " + search_path)
	
	# Check if directory exists
	if not DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(search_path.trim_suffix("/"))):
		if search_path != "res://":
			printerr("Directory does not exist: " + search_path)
			ctx.quit(1)
	
	# Find all scene files
	var scenes: Array = _find_scenes_recursive(search_path, recursive, ctx.debug_mode)
	
	# Convert to relative paths (remove res:// prefix)
	var result: Array = []
	for scene_path: String in scenes:
		var relative_path: String = scene_path
		if relative_path.begins_with("res://"):
			relative_path = relative_path.substr(6)  # Remove "res://"
		result.append(relative_path)
	
	# Output as JSON
	var json_output: String = JSON.stringify({"scenes": result, "count": result.size()})
	print(json_output)


## Helper function to recursively find scene files
static func _find_scenes_recursive(path: String, recursive: bool, debug_mode: bool) -> Array:
	var scenes: Array = []
	var dir: DirAccess = DirAccess.open(path)
	
	if dir == null:
		if debug_mode:
			print("[DEBUG] Could not open directory: " + path)
		return scenes
	
	dir.list_dir_begin()
	var file_name: String = dir.get_next()
	
	while file_name != "":
		if file_name.begins_with("."):
			file_name = dir.get_next()
			continue
		
		var full_path: String = path + file_name
		
		if dir.current_is_dir():
			if recursive:
				scenes.append_array(_find_scenes_recursive(full_path + "/", true, debug_mode))
		elif file_name.ends_with(".tscn"):
			scenes.append(full_path)
			if debug_mode:
				print("[DEBUG] Found scene: " + full_path)
		
		file_name = dir.get_next()
	
	dir.list_dir_end()
	return scenes


## Get the node tree structure of a scene
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - max_depth: int - Maximum depth to traverse (-1 for unlimited)
static func get_scene_tree_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var max_depth: int = params.get("max_depth", -1)
	
	if ctx.debug_mode:
		print("[DEBUG] Getting scene tree for: " + scene_path)
		print("[DEBUG] Max depth: " + str(max_depth))
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Check if scene exists
	if not FileAccess.file_exists(full_scene_path):
		printerr("Scene file does not exist: " + full_scene_path)
		ctx.quit(1)
	
	# Load and instantiate the scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	if not scene_root:
		printerr("Failed to instantiate scene: " + full_scene_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("[DEBUG] Scene loaded and instantiated")
	
	# Build the tree structure
	var tree: Dictionary = _build_node_tree(scene_root, 0, max_depth, ctx.debug_mode)
	
	# Clean up
	scene_root.queue_free()
	
	# Output as JSON
	var json_output: String = JSON.stringify(tree)
	print(json_output)


## Helper function to recursively build node tree structure
static func _build_node_tree(node: Node, current_depth: int, max_depth: int, debug_mode: bool) -> Dictionary:
	var node_info: Dictionary = {
		"name": node.name,
		"type": node.get_class(),
		"path": "root" if current_depth == 0 else str(node.get_path()),
	}
	
	# Add script info if attached
	var script: Script = node.get_script()
	if script:
		var script_path: String = script.resource_path
		if script_path.begins_with("res://"):
			script_path = script_path.substr(6)
		node_info["script"] = script_path
	
	# Add children if within depth limit
	if max_depth == -1 or current_depth < max_depth:
		var children: Array = []
		for child: Node in node.get_children():
			children.append(_build_node_tree(child, current_depth + 1, max_depth, debug_mode))
		if children.size() > 0:
			node_info["children"] = children
	elif node.get_child_count() > 0:
		node_info["children_count"] = node.get_child_count()
	
	return node_info


## Get all properties of a specific node in a scene
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - node_path: String - Path to the node within the scene
##   - include_inherited: bool - Whether to include inherited properties
static func get_node_properties_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var node_path: String = params.node_path
	var include_inherited: bool = params.get("include_inherited", false)
	
	if ctx.debug_mode:
		print("[DEBUG] Getting properties for node: " + node_path)
		print("[DEBUG] In scene: " + scene_path)
		print("[DEBUG] Include inherited: " + str(include_inherited))
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Check if scene exists
	if not FileAccess.file_exists(full_scene_path):
		printerr("Scene file does not exist: " + full_scene_path)
		ctx.quit(1)
	
	# Load and instantiate the scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	if not scene_root:
		printerr("Failed to instantiate scene: " + full_scene_path)
		ctx.quit(1)
	
	# Find the target node
	var target_node: Node = null
	if node_path == "root" or node_path.is_empty():
		target_node = scene_root
	else:
		var search_path: String = node_path
		if search_path.begins_with("root/"):
			search_path = search_path.substr(5)  # Remove "root/"
		target_node = scene_root.get_node_or_null(search_path)
	
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("[DEBUG] Found node: " + target_node.name + " of type: " + target_node.get_class())
	
	# Get properties
	var properties: Array = []
	var property_list: Array = target_node.get_property_list()
	
	for prop: Dictionary in property_list:
		var prop_name: String = prop["name"]
		var prop_usage: int = prop["usage"]
		
		# Skip internal properties unless include_inherited is true
		if not include_inherited:
			# Only include properties that are PROPERTY_USAGE_STORAGE (can be saved)
			# or PROPERTY_USAGE_EDITOR (visible in editor)
			if not (prop_usage & PROPERTY_USAGE_STORAGE or prop_usage & PROPERTY_USAGE_EDITOR):
				continue
			# Skip properties starting with underscore (internal)
			if prop_name.begins_with("_"):
				continue
		
		# Get property value
		var prop_value: Variant = target_node.get(prop_name)
		var prop_type: int = prop["type"]
		
		# Convert value to JSON-serializable format
		var serialized_value: Variant = _serialize_property_value(prop_value, prop_type)
		
		properties.append({
			"name": prop_name,
			"type": _get_type_name(prop_type),
			"value": serialized_value,
		})
	
	# Clean up
	scene_root.queue_free()
	
	# Build result
	var result: Dictionary = {
		"node_name": target_node.name,
		"node_type": target_node.get_class(),
		"node_path": node_path,
		"properties": properties,
		"property_count": properties.size(),
	}
	
	# Add script info if attached
	var script: Script = target_node.get_script()
	if script:
		var script_path: String = script.resource_path
		if script_path.begins_with("res://"):
			script_path = script_path.substr(6)
		result["script"] = script_path
	
	# Output as JSON
	var json_output: String = JSON.stringify(result)
	print(json_output)


## Helper function to serialize property values to JSON-compatible format
static func _serialize_property_value(value: Variant, prop_type: int) -> Variant:
	match prop_type:
		TYPE_NIL:
			return null
		TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING:
			return value
		TYPE_VECTOR2:
			var v: Vector2 = value
			return {"x": v.x, "y": v.y}
		TYPE_VECTOR2I:
			var v: Vector2i = value
			return {"x": v.x, "y": v.y}
		TYPE_RECT2:
			var r: Rect2 = value
			return {"position": {"x": r.position.x, "y": r.position.y}, "size": {"x": r.size.x, "y": r.size.y}}
		TYPE_VECTOR3:
			var v: Vector3 = value
			return {"x": v.x, "y": v.y, "z": v.z}
		TYPE_VECTOR3I:
			var v: Vector3i = value
			return {"x": v.x, "y": v.y, "z": v.z}
		TYPE_TRANSFORM2D:
			var t: Transform2D = value
			return {"x": {"x": t.x.x, "y": t.x.y}, "y": {"x": t.y.x, "y": t.y.y}, "origin": {"x": t.origin.x, "y": t.origin.y}}
		TYPE_COLOR:
			var c: Color = value
			return {"r": c.r, "g": c.g, "b": c.b, "a": c.a, "hex": c.to_html()}
		TYPE_NODE_PATH:
			return str(value)
		TYPE_OBJECT:
			if value == null:
				return null
			if value is Resource:
				var res: Resource = value
				if res.resource_path.is_empty():
					return {"type": res.get_class(), "path": null}
				var path: String = res.resource_path
				if path.begins_with("res://"):
					path = path.substr(6)
				return {"type": res.get_class(), "path": path}
			return {"type": value.get_class()}
		TYPE_ARRAY, TYPE_PACKED_STRING_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_FLOAT32_ARRAY:
			var arr: Array = []
			for item: Variant in value:
				arr.append(str(item))
			return arr
		TYPE_DICTIONARY:
			var dict: Dictionary = {}
			for key: Variant in value:
				dict[str(key)] = str(value[key])
			return dict
		_:
			return str(value)


## Helper function to get type name from type constant
static func _get_type_name(type_id: int) -> String:
	match type_id:
		TYPE_NIL: return "null"
		TYPE_BOOL: return "bool"
		TYPE_INT: return "int"
		TYPE_FLOAT: return "float"
		TYPE_STRING: return "String"
		TYPE_VECTOR2: return "Vector2"
		TYPE_VECTOR2I: return "Vector2i"
		TYPE_RECT2: return "Rect2"
		TYPE_RECT2I: return "Rect2i"
		TYPE_VECTOR3: return "Vector3"
		TYPE_VECTOR3I: return "Vector3i"
		TYPE_TRANSFORM2D: return "Transform2D"
		TYPE_VECTOR4: return "Vector4"
		TYPE_VECTOR4I: return "Vector4i"
		TYPE_PLANE: return "Plane"
		TYPE_QUATERNION: return "Quaternion"
		TYPE_AABB: return "AABB"
		TYPE_BASIS: return "Basis"
		TYPE_TRANSFORM3D: return "Transform3D"
		TYPE_PROJECTION: return "Projection"
		TYPE_COLOR: return "Color"
		TYPE_STRING_NAME: return "StringName"
		TYPE_NODE_PATH: return "NodePath"
		TYPE_RID: return "RID"
		TYPE_OBJECT: return "Object"
		TYPE_CALLABLE: return "Callable"
		TYPE_SIGNAL: return "Signal"
		TYPE_DICTIONARY: return "Dictionary"
		TYPE_ARRAY: return "Array"
		TYPE_PACKED_BYTE_ARRAY: return "PackedByteArray"
		TYPE_PACKED_INT32_ARRAY: return "PackedInt32Array"
		TYPE_PACKED_INT64_ARRAY: return "PackedInt64Array"
		TYPE_PACKED_FLOAT32_ARRAY: return "PackedFloat32Array"
		TYPE_PACKED_FLOAT64_ARRAY: return "PackedFloat64Array"
		TYPE_PACKED_STRING_ARRAY: return "PackedStringArray"
		TYPE_PACKED_VECTOR2_ARRAY: return "PackedVector2Array"
		TYPE_PACKED_VECTOR3_ARRAY: return "PackedVector3Array"
		TYPE_PACKED_COLOR_ARRAY: return "PackedColorArray"
		TYPE_PACKED_VECTOR4_ARRAY: return "PackedVector4Array"
		_: return "Unknown"


# ============================================================================
# Scene Manipulation Operations
# ============================================================================


## Set a property on a node in a scene
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - node_path: String - Path to the node
##   - property_name: String - Name of the property to set
##   - property_value: Variant - Value to set
static func set_node_property_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var node_path: String = params.node_path
	var property_name: String = params.property_name
	var property_value: Variant = params.property_value
	
	if ctx.debug_mode:
		print("[DEBUG] Setting property: " + property_name + " on node: " + node_path)
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Load and instantiate scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	
	# Find target node
	var target_node: Node = _find_node_by_path(scene_root, node_path)
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	# Convert property value if needed (e.g., dict to Vector2/3)
	var converted_value: Variant = _convert_property_value(property_value, target_node, property_name)
	
	# Set the property
	target_node.set(property_name, converted_value)
	
	if ctx.debug_mode:
		print("[DEBUG] Property set successfully")
	
	# Save scene
	_save_scene(ctx, scene_root, full_scene_path)
	print("Property '" + property_name + "' set successfully on node '" + node_path + "'")


## Delete a node from a scene
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - node_path: String - Path to the node to delete
static func delete_node_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var node_path: String = params.node_path
	
	if ctx.debug_mode:
		print("[DEBUG] Deleting node: " + node_path)
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Load and instantiate scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	
	# Find target node
	var target_node: Node = _find_node_by_path(scene_root, node_path)
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	if target_node == scene_root:
		printerr("Cannot delete root node")
		scene_root.queue_free()
		ctx.quit(1)
	
	# Remove and free the node
	var parent: Node = target_node.get_parent()
	parent.remove_child(target_node)
	target_node.queue_free()
	
	if ctx.debug_mode:
		print("[DEBUG] Node removed from tree")
	
	# Save scene
	_save_scene(ctx, scene_root, full_scene_path)
	print("Node '" + node_path + "' deleted successfully")


## Instance a scene as a child of another scene
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - target_scene_path: String - Path to the target scene
##   - source_scene_path: String - Path to the scene to instance
##   - parent_node_path: String - Path to parent node (default: "root")
##   - instance_name: String - Optional custom name for the instance
static func instance_scene_op(ctx: SceneTree, params: Dictionary) -> void:
	var target_scene_path: String = params.target_scene_path
	var source_scene_path: String = params.source_scene_path
	var parent_node_path: String = params.get("parent_node_path", "root")
	var instance_name: String = params.get("instance_name", "")
	
	if ctx.debug_mode:
		print("[DEBUG] Instancing scene: " + source_scene_path + " into " + target_scene_path)
	
	# Normalize paths
	var full_target_path: String = target_scene_path
	if not full_target_path.begins_with("res://"):
		full_target_path = "res://" + full_target_path
	
	var full_source_path: String = source_scene_path
	if not full_source_path.begins_with("res://"):
		full_source_path = "res://" + full_source_path
	
	# Load target scene
	var target_scene: PackedScene = load(full_target_path)
	if not target_scene:
		printerr("Failed to load target scene: " + full_target_path)
		ctx.quit(1)
	
	var scene_root: Node = target_scene.instantiate()
	
	# Load source scene
	var source_scene: PackedScene = load(full_source_path)
	if not source_scene:
		printerr("Failed to load source scene: " + full_source_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	# Find parent node
	var parent_node: Node = _find_node_by_path(scene_root, parent_node_path)
	if not parent_node:
		printerr("Parent node not found: " + parent_node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	# Instance the source scene
	var instance: Node = source_scene.instantiate()
	
	# Set custom name if provided
	if not instance_name.is_empty():
		instance.name = instance_name
	
	# Add to parent
	parent_node.add_child(instance)
	instance.owner = scene_root
	
	# Also set owner for all descendants
	_set_owner_recursive(instance, scene_root)
	
	if ctx.debug_mode:
		print("[DEBUG] Scene instanced as: " + instance.name)
	
	# Save scene
	_save_scene(ctx, scene_root, full_target_path)
	print("Scene '" + source_scene_path + "' instanced as '" + instance.name + "'")


## Rename a node in a scene
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - node_path: String - Path to the node to rename
##   - new_name: String - New name for the node
static func rename_node_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var node_path: String = params.node_path
	var new_name: String = params.new_name
	
	if ctx.debug_mode:
		print("[DEBUG] Renaming node: " + node_path + " to " + new_name)
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Load and instantiate scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	
	# Find target node
	var target_node: Node = _find_node_by_path(scene_root, node_path)
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	var old_name: String = target_node.name
	target_node.name = new_name
	
	if ctx.debug_mode:
		print("[DEBUG] Node renamed from " + old_name + " to " + new_name)
	
	# Save scene
	_save_scene(ctx, scene_root, full_scene_path)
	print("Node renamed from '" + old_name + "' to '" + new_name + "'")


## Move a node to a new parent (reparent)
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - node_path: String - Path to the node to move
##   - new_parent_path: String - Path to the new parent node
static func move_node_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var node_path: String = params.node_path
	var new_parent_path: String = params.new_parent_path
	
	if ctx.debug_mode:
		print("[DEBUG] Moving node: " + node_path + " to parent " + new_parent_path)
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Load and instantiate scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	
	# Find target node
	var target_node: Node = _find_node_by_path(scene_root, node_path)
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	if target_node == scene_root:
		printerr("Cannot move root node")
		scene_root.queue_free()
		ctx.quit(1)
	
	# Find new parent
	var new_parent: Node = _find_node_by_path(scene_root, new_parent_path)
	if not new_parent:
		printerr("New parent not found: " + new_parent_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	# Reparent the node
	var old_parent: Node = target_node.get_parent()
	old_parent.remove_child(target_node)
	new_parent.add_child(target_node)
	target_node.owner = scene_root
	
	if ctx.debug_mode:
		print("[DEBUG] Node moved successfully")
	
	# Save scene
	_save_scene(ctx, scene_root, full_scene_path)
	print("Node '" + target_node.name + "' moved to parent '" + new_parent_path + "'")


## Duplicate a node in a scene
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - node_path: String - Path to the node to duplicate
##   - new_name: String - Optional name for the duplicate
static func duplicate_node_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var node_path: String = params.node_path
	var new_name: String = params.get("new_name", "")
	
	if ctx.debug_mode:
		print("[DEBUG] Duplicating node: " + node_path)
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Load and instantiate scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	
	# Find target node
	var target_node: Node = _find_node_by_path(scene_root, node_path)
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	if target_node == scene_root:
		printerr("Cannot duplicate root node")
		scene_root.queue_free()
		ctx.quit(1)
	
	# Duplicate the node
	var duplicate: Node = target_node.duplicate()
	
	# Set name
	if not new_name.is_empty():
		duplicate.name = new_name
	
	# Add to same parent
	var parent: Node = target_node.get_parent()
	parent.add_child(duplicate)
	duplicate.owner = scene_root
	
	# Set owner for descendants
	_set_owner_recursive(duplicate, scene_root)
	
	if ctx.debug_mode:
		print("[DEBUG] Node duplicated as: " + duplicate.name)
	
	# Save scene
	_save_scene(ctx, scene_root, full_scene_path)
	print("Node '" + target_node.name + "' duplicated as '" + duplicate.name + "'")


## Add a node to a group
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - scene_path: String - Path to the scene file
##   - node_path: String - Path to the node
##   - group_name: String - Name of the group
##   - persistent: bool - Whether the group persists when saved (default: true)
static func add_to_group_op(ctx: SceneTree, params: Dictionary) -> void:
	var scene_path: String = params.scene_path
	var node_path: String = params.node_path
	var group_name: String = params.group_name
	var persistent: bool = params.get("persistent", true)
	
	if ctx.debug_mode:
		print("[DEBUG] Adding node to group: " + group_name)
	
	# Normalize scene path
	var full_scene_path: String = scene_path
	if not full_scene_path.begins_with("res://"):
		full_scene_path = "res://" + full_scene_path
	
	# Load and instantiate scene
	var scene: PackedScene = load(full_scene_path)
	if not scene:
		printerr("Failed to load scene: " + full_scene_path)
		ctx.quit(1)
	
	var scene_root: Node = scene.instantiate()
	
	# Find target node
	var target_node: Node = _find_node_by_path(scene_root, node_path)
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	# Add to group
	target_node.add_to_group(group_name, persistent)
	
	if ctx.debug_mode:
		print("[DEBUG] Node added to group: " + group_name)
	
	# Save scene
	_save_scene(ctx, scene_root, full_scene_path)
	print("Node '" + target_node.name + "' added to group '" + group_name + "'")


# ============================================================================
# Helper Functions for Scene Manipulation
# ============================================================================


## Find a node by path (handles "root" and "root/Child" format)
static func _find_node_by_path(scene_root: Node, node_path: String) -> Node:
	if node_path == "root" or node_path.is_empty():
		return scene_root
	
	var search_path: String = node_path
	if search_path.begins_with("root/"):
		search_path = search_path.substr(5)  # Remove "root/"
	
	return scene_root.get_node_or_null(search_path)


## Convert JSON property value to appropriate Godot type
static func _convert_property_value(value: Variant, node: Node, property_name: String) -> Variant:
	# Get property info to determine expected type
	var prop_list: Array = node.get_property_list()
	var expected_type: int = TYPE_NIL
	
	for prop: Dictionary in prop_list:
		if prop["name"] == property_name:
			expected_type = prop["type"]
			break
	
	# If value is a dictionary, try to convert to Vector2/3/Color
	if value is Dictionary:
		var dict: Dictionary = value
		if dict.has("x") and dict.has("y"):
			if dict.has("z"):
				return Vector3(dict["x"], dict["y"], dict["z"])
			else:
				return Vector2(dict["x"], dict["y"])
		elif dict.has("r") and dict.has("g") and dict.has("b"):
			var a: float = dict.get("a", 1.0)
			return Color(dict["r"], dict["g"], dict["b"], a)
	
	# Handle string to Color conversion (hex)
	if value is String and expected_type == TYPE_COLOR:
		var color_str: String = value
		if color_str.begins_with("#"):
			return Color.html(color_str)
	
	return value


## Save a scene to disk
static func _save_scene(ctx: SceneTree, scene_root: Node, full_scene_path: String) -> void:
	var packed_scene: PackedScene = PackedScene.new()
	var result: int = packed_scene.pack(scene_root)
	
	if result != OK:
		printerr("Failed to pack scene: " + str(result))
		scene_root.queue_free()
		ctx.quit(1)
	
	var save_error: int = ResourceSaver.save(packed_scene, full_scene_path)
	if save_error != OK:
		printerr("Failed to save scene: " + str(save_error))
		scene_root.queue_free()
		ctx.quit(1)
	
	scene_root.queue_free()


## Recursively set owner for all children
static func _set_owner_recursive(node: Node, owner: Node) -> void:
	for child: Node in node.get_children():
		child.owner = owner
		_set_owner_recursive(child, owner)
