# Script operations module
# Handles: attach_script, detach_script, list_scripts
# Corresponds to: test_script_attachment.py, test_e2e_scripts.py, test_scripting_resources.py
class_name ScriptOps


## Attach a script to a node in a scene
static func attach_script(ctx: SceneTree, params: Dictionary) -> void:
	print("Attaching script to node in 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)
	
	# Ensure the script path starts with res:// for Godot's resource system
	var full_script_path: String = params.script_path
	if not full_script_path.begins_with("res://"):
		full_script_path = "res://" + full_script_path
	
	if ctx.debug_mode:
		print("Full script path (with res://): " + full_script_path)
	
	# Check if the scene file exists
	var scene_file_check: bool = FileAccess.file_exists(full_scene_path)
	if ctx.debug_mode:
		print("Scene file exists check: " + str(scene_file_check))
	
	if not scene_file_check:
		printerr("Scene file does not exist at: " + full_scene_path)
		var absolute_path: String = ProjectSettings.globalize_path(full_scene_path)
		printerr("Absolute file path that doesn't exist: " + absolute_path)
		ctx.quit(1)
	
	# Check if the script file exists
	var script_file_check: bool = FileAccess.file_exists(full_script_path)
	if ctx.debug_mode:
		print("Script file exists check: " + str(script_file_check))
	
	if not script_file_check:
		printerr("Script file does not exist at: " + full_script_path)
		var absolute_path: String = ProjectSettings.globalize_path(full_script_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")
	
	# Find the target node
	var node_path: String = params.node_path
	if ctx.debug_mode:
		print("Original node path: " + node_path)
	
	var target_node: Node = scene_root
	if node_path != "root" and node_path != "":
		var relative_path: String = node_path
		if relative_path.begins_with("root/"):
			relative_path = relative_path.substr(5)  # Remove "root/" prefix
		if ctx.debug_mode:
			print("Relative node path: " + relative_path)
		target_node = scene_root.get_node(relative_path)
	
	if not target_node:
		printerr("Node not found: " + node_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("Target node found: " + target_node.name + " of type: " + target_node.get_class())
	
	# Load the script
	var script: Script = load(full_script_path)
	if not script:
		printerr("Failed to load script: " + full_script_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("Script loaded successfully")
	
	# Attach the script to the node
	target_node.set_script(script)
	if ctx.debug_mode:
		print("Script attached to node")
	
	# Pack and save 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:
		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:
			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("Script attached successfully to node: " + node_path)
					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("Script attached successfully to node: " + node_path)
		else:
			printerr("Failed to save scene: " + str(error))
	else:
		printerr("Failed to pack scene: " + str(result))


## Detach a script from 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
static func detach_script_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] Detaching script from 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
	
	# 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 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 = scene_root
	if node_path != "root" and not node_path.is_empty():
		var relative_path: String = node_path
		if relative_path.begins_with("root/"):
			relative_path = relative_path.substr(5)
		target_node = scene_root.get_node_or_null(relative_path)
	
	if not target_node:
		printerr("Node not found: " + node_path)
		scene_root.queue_free()
		ctx.quit(1)
	
	# Detach script
	target_node.set_script(null)
	
	if ctx.debug_mode:
		print("[DEBUG] Script detached from node")
	
	# Save scene
	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()
	print("Script detached successfully from node: " + node_path)


## List all GDScript files in the project
## @param ctx: SceneTree - The main script context
## @param params: Dictionary - Parameters from Python:
##   - directory: String - Optional subdirectory to search in
##   - recursive: bool - Whether to search recursively (default: true)
static func list_scripts_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 scripts in directory: " + directory)
	
	# 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 += "/"
	
	# Find all script files
	var scripts: Array = _find_files_by_extension(search_path, ".gd", recursive, ctx.debug_mode)
	
	# Convert to relative paths
	var result: Array = []
	for script_path: String in scripts:
		var relative_path: String = script_path
		if relative_path.begins_with("res://"):
			relative_path = relative_path.substr(6)
		result.append(relative_path)
	
	# Output as JSON
	var json_output: String = JSON.stringify({"scripts": result, "count": result.size()})
	print(json_output)


## Helper function to recursively find files by extension
static func _find_files_by_extension(path: String, extension: String, recursive: bool, debug_mode: bool) -> Array:
	var files: Array = []
	var dir: DirAccess = DirAccess.open(path)
	
	if dir == null:
		if debug_mode:
			print("[DEBUG] Could not open directory: " + path)
		return files
	
	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:
				files.append_array(_find_files_by_extension(full_path + "/", extension, true, debug_mode))
		elif file_name.ends_with(extension):
			files.append(full_path)
			if debug_mode:
				print("[DEBUG] Found file: " + full_path)
		
		file_name = dir.get_next()
	
	dir.list_dir_end()
	return files
