@tool
extends Node

## Reads and manipulates the scene tree in the editor.


func register(server: Node) -> void:
	server.register_handler("get_scene_tree", _get_scene_tree)
	server.register_handler("add_node", _add_node)
	server.register_handler("add_nodes", _add_nodes)
	server.register_handler("delete_node", _delete_node)
	server.register_handler("move_node", _move_node)
	server.register_handler("duplicate_node", _duplicate_node)
	server.register_handler("create_scene", _create_scene)
	server.register_handler("open_scene", _open_scene)


func _get_scene_tree(_args: Dictionary) -> Dictionary:
	var root := EditorInterface.get_edited_scene_root()
	if root == null:
		return {"ok": false, "error": "No scene is currently open in the editor"}

	var tree_str := _build_tree(root, 0)
	return {"ok": true, "type": "text", "result": tree_str}


func _build_tree(node: Node, depth: int) -> String:
	var indent := "  ".repeat(depth)
	var line := "%s%s (%s)" % [indent, node.name, node.get_class()]

	var script: Script = node.get_script()
	if script and script.resource_path != "":
		line += " [%s]" % script.resource_path

	var result := line + "\n"
	for child in node.get_children():
		result += _build_tree(child, depth + 1)
	return result


# ------------------------------------------------------------------
# Single node
# ------------------------------------------------------------------

func _add_node(args: Dictionary) -> Dictionary:
	var parent_path: String = args.get("parent", "")
	var type_name: String = args.get("type", "Node")
	var node_name: String = args.get("name", "")

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

	var parent: Node = root if parent_path.is_empty() else root.get_node_or_null(parent_path)
	if parent == null:
		return {"ok": false, "error": "Parent node not found: %s" % parent_path}

	if not ClassDB.class_exists(type_name):
		return {"ok": false, "error": "Unknown node type: %s" % type_name}

	var new_node: Node = ClassDB.instantiate(type_name)
	if node_name != "":
		new_node.name = node_name

	parent.add_child(new_node)
	new_node.owner = root
	return {"ok": true, "type": "text", "result": "Created %s (%s) under %s" % [new_node.name, type_name, parent.name]}


# ------------------------------------------------------------------
# Batch add nodes
# ------------------------------------------------------------------

func _add_nodes(args: Dictionary) -> Dictionary:
	"""Create an entire node hierarchy from a JSON tree.

	Expected format:
	{
	  "nodes": [
	    {
	      "name": "Player",
	      "type": "CharacterBody2D",
	      "parent": "",
	      "children": [
	        {"name": "Sprite", "type": "Sprite2D"},
	        {"name": "Collision", "type": "CollisionShape2D"}
	      ]
	    }
	  ]
	}
	"""
	var nodes: Array = args.get("nodes", [])
	if nodes.is_empty():
		return {"ok": false, "error": "Missing 'nodes' array"}

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

	var count := 0
	var errors: PackedStringArray = []

	for node_spec in nodes:
		var result := _create_node_recursive(node_spec, root, root)
		count += result["count"]
		errors.append_array(result["errors"])

	var msg := "Created %d node(s)" % count
	if errors.size() > 0:
		msg += "\nErrors:\n" + "\n".join(errors)

	return {"ok": errors.size() == 0, "type": "text", "result": msg}


func _create_node_recursive(spec: Dictionary, parent: Node, root: Node) -> Dictionary:
	var count := 0
	var errors: PackedStringArray = []

	var type_name: String = spec.get("type", "Node")
	var node_name: String = spec.get("name", "")
	var parent_path: String = spec.get("parent", "")

	# Resolve parent if specified
	var actual_parent := parent
	if not parent_path.is_empty():
		var resolved := root.get_node_or_null(parent_path)
		if resolved != null:
			actual_parent = resolved
		else:
			errors.append("Parent not found: %s" % parent_path)
			return {"count": 0, "errors": errors}

	if not ClassDB.class_exists(type_name):
		errors.append("Unknown type: %s" % type_name)
		return {"count": 0, "errors": errors}

	var new_node: Node = ClassDB.instantiate(type_name)
	if node_name != "":
		new_node.name = node_name

	actual_parent.add_child(new_node)
	new_node.owner = root
	count += 1

	# Set properties if provided
	var properties: Dictionary = spec.get("properties", {})
	for key in properties:
		new_node.set(key, properties[key])

	# Recurse into children
	var children: Array = spec.get("children", [])
	for child_spec in children:
		var child_result := _create_node_recursive(child_spec, new_node, root)
		count += child_result["count"]
		errors.append_array(child_result["errors"])

	return {"count": count, "errors": errors}


# ------------------------------------------------------------------
# Delete / Move / Duplicate
# ------------------------------------------------------------------

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

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

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

	if target == root:
		return {"ok": false, "error": "Cannot delete the scene root"}

	var name := target.name
	target.queue_free()
	return {"ok": true, "type": "text", "result": "Deleted node: %s" % name}


func _move_node(args: Dictionary) -> Dictionary:
	var node_path: String = args.get("path", "")
	var new_parent_path: String = args.get("new_parent", "")

	if node_path.is_empty() or new_parent_path.is_empty():
		return {"ok": false, "error": "Missing 'path' or 'new_parent' argument"}

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

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

	var new_parent: Node = root.get_node_or_null(new_parent_path)
	if new_parent == null:
		return {"ok": false, "error": "New parent not found: %s" % new_parent_path}

	target.reparent(new_parent)
	target.owner = root
	return {"ok": true, "type": "text", "result": "Moved %s under %s" % [target.name, new_parent.name]}


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

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

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

	var dup: Node = target.duplicate()
	target.get_parent().add_child(dup)
	dup.owner = root
	return {"ok": true, "type": "text", "result": "Duplicated %s as %s" % [target.name, dup.name]}


# ------------------------------------------------------------------
# Create / Open scene
# ------------------------------------------------------------------

func _create_scene(args: Dictionary) -> Dictionary:
	var scene_path: String = args.get("path", "")
	var root_type: String = args.get("root_type", "Node2D")
	var root_name: String = args.get("root_name", "")

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

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

	if not scene_path.ends_with(".tscn"):
		scene_path += ".tscn"

	if not ClassDB.class_exists(root_type):
		return {"ok": false, "error": "Unknown root type: %s" % root_type}

	# Create directories if needed
	var dir_path := scene_path.get_base_dir()
	if not DirAccess.dir_exists_absolute(dir_path):
		DirAccess.make_dir_recursive_absolute(dir_path)

	# Create root node
	var root_node: Node = ClassDB.instantiate(root_type)
	if root_name != "":
		root_node.name = root_name
	else:
		root_node.name = scene_path.get_file().get_basename()

	# Pack and save
	var scene := PackedScene.new()
	scene.pack(root_node)
	var err := ResourceSaver.save(scene, scene_path)
	root_node.queue_free()

	if err != OK:
		return {"ok": false, "error": "Failed to save scene: %s (error %d)" % [scene_path, err]}

	# Rescan filesystem and open the scene
	EditorInterface.get_resource_filesystem().scan()
	EditorInterface.open_scene_from_path(scene_path)

	return {"ok": true, "type": "text", "result": "Created and opened scene: %s" % scene_path}


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

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

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

	EditorInterface.open_scene_from_path(scene_path)
	return {"ok": true, "type": "text", "result": "Opened scene: %s" % scene_path}
