@tool
extends Node

## Views, writes, executes, and attaches GDScript in the editor context.


func register(server: Node) -> void:
	server.register_handler("view_script", _view_script)
	server.register_handler("execute_script", _execute_script)
	server.register_handler("write_script", _write_script)
	server.register_handler("attach_script", _attach_script)


func _view_script(args: Dictionary) -> Dictionary:
	var script_path: String = args.get("path", "")
	if script_path.is_empty():
		return {"ok": false, "error": "Missing 'path' argument"}

	if not FileAccess.file_exists(script_path):
		return {"ok": false, "error": "Script not found: %s" % script_path}

	var file := FileAccess.open(script_path, FileAccess.READ)
	if file == null:
		return {"ok": false, "error": "Cannot open file: %s" % script_path}

	var content := file.get_as_text()
	file.close()
	return {"ok": true, "type": "text", "result": content}


func _execute_script(args: Dictionary) -> Dictionary:
	var code: String = args.get("code", "")
	if code.is_empty():
		return {"ok": false, "error": "Missing 'code' argument"}

	var script := GDScript.new()
	script.source_code = """
extends RefCounted

func _run():
%s
""" % _indent_code(code)

	var err := script.reload()
	if err != OK:
		return {"ok": false, "error": "Script compilation error (code %d)" % err}

	var instance = script.new()
	if instance == null:
		return {"ok": false, "error": "Failed to instantiate script"}

	var result = instance._run()
	if result == null:
		return {"ok": true, "type": "text", "result": "Script executed (no return value)"}

	return {"ok": true, "type": "text", "result": str(result)}


func _write_script(args: Dictionary) -> Dictionary:
	var script_path: String = args.get("path", "")
	var content: String = args.get("content", "")

	if script_path.is_empty():
		return {"ok": false, "error": "Missing 'path' argument"}
	if content.is_empty():
		return {"ok": false, "error": "Missing 'content' argument"}

	# Ensure path starts with res://
	if not script_path.begins_with("res://"):
		script_path = "res://" + script_path

	# Create directories if needed
	var dir_path := script_path.get_base_dir()
	if not DirAccess.dir_exists_absolute(dir_path):
		var err := DirAccess.make_dir_recursive_absolute(dir_path)
		if err != OK:
			return {"ok": false, "error": "Cannot create directory: %s (error %d)" % [dir_path, err]}

	var file := FileAccess.open(script_path, FileAccess.WRITE)
	if file == null:
		return {"ok": false, "error": "Cannot write to: %s (error %d)" % [script_path, FileAccess.get_open_error()]}

	file.store_string(content)
	file.close()

	# Tell the editor to reimport/rescan
	EditorInterface.get_resource_filesystem().scan()

	return {"ok": true, "type": "text", "result": "Script written: %s" % script_path}


func _attach_script(args: Dictionary) -> Dictionary:
	var node_path: String = args.get("node", "")
	var script_path: String = args.get("script", "")

	if node_path.is_empty() or script_path.is_empty():
		return {"ok": false, "error": "Missing 'node' or 'script' argument"}

	if not script_path.begins_with("res://"):
		script_path = "res://" + script_path

	var root := EditorInterface.get_edited_scene_root()
	if root == null:
		return {"ok": false, "error": "No scene open"}

	var target: Node
	if node_path == "." or node_path.is_empty():
		target = root
	else:
		target = root.get_node_or_null(node_path)
	if target == null:
		return {"ok": false, "error": "Node not found: %s" % node_path}

	if not FileAccess.file_exists(script_path):
		return {"ok": false, "error": "Script file not found: %s" % script_path}

	var script := load(script_path) as Script
	if script == null:
		return {"ok": false, "error": "Failed to load script: %s" % script_path}

	target.set_script(script)
	return {"ok": true, "type": "text", "result": "Attached %s to %s" % [script_path, target.name]}


func _indent_code(code: String) -> String:
	var lines := code.split("\n")
	var indented: PackedStringArray = []
	for line in lines:
		indented.append("\t" + line)
	return "\n".join(indented)
