@tool
extends Node

## Inspects node properties and allows updating them, including Resource types.


func register(server: Node) -> void:
	server.register_handler("inspect_node", _inspect_node)
	server.register_handler("update_property", _update_property)


func _inspect_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 info := _gather_node_info(target)
	return {"ok": true, "type": "text", "result": info}


func _gather_node_info(node: Node) -> String:
	var lines: PackedStringArray = []
	lines.append("Node: %s" % node.name)
	lines.append("Type: %s" % node.get_class())
	lines.append("Path: %s" % str(node.get_path()))

	var script: Script = node.get_script()
	if script:
		lines.append("Script: %s" % script.resource_path)

	lines.append("")
	lines.append("Properties:")

	var prop_list := node.get_property_list()
	for prop in prop_list:
		if prop["usage"] & PROPERTY_USAGE_EDITOR == 0:
			continue
		var name: String = prop["name"]
		if name.begins_with("_") or name.begins_with("metadata/"):
			continue

		var value = node.get(name)
		var value_str := _format_value(value)
		lines.append("  %s = %s" % [name, value_str])

	var signals := node.get_signal_list()
	if signals.size() > 0:
		lines.append("")
		lines.append("Signals:")
		for sig in signals:
			var connections := node.get_signal_connection_list(sig["name"])
			if connections.size() > 0:
				lines.append("  %s → %d connection(s)" % [sig["name"], connections.size()])

	var groups := node.get_groups()
	if groups.size() > 0:
		lines.append("")
		lines.append("Groups: %s" % ", ".join(groups))

	return "\n".join(lines)


func _format_value(value) -> String:
	if value == null:
		return "null"
	if value is String:
		return '"%s"' % value
	if value is Vector2 or value is Vector3 or value is Color or value is Rect2:
		return str(value)
	if value is Resource:
		if value.resource_path != "":
			return "<%s> %s" % [value.get_class(), value.resource_path]
		return "<%s>" % value.get_class()
	if value is NodePath:
		return "NodePath(%s)" % str(value)
	return str(value)


# ------------------------------------------------------------------
# Update property — with Resource support
# ------------------------------------------------------------------

func _update_property(args: Dictionary) -> Dictionary:
	var node_path: String = args.get("path", "")
	var prop_name: String = args.get("property", "")
	var prop_value = args.get("value")

	if node_path.is_empty() or prop_name.is_empty():
		return {"ok": false, "error": "Missing 'path' or 'property' 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 current = target.get(prop_name)
	var converted = _convert_value(prop_value, current, prop_name)

	if converted == null and prop_value != null:
		return {"ok": false, "error": "Could not convert value for property '%s'" % prop_name}

	target.set(prop_name, converted)
	return {"ok": true, "type": "text", "result": "Set %s.%s = %s" % [target.name, prop_name, str(converted)]}


func _convert_value(value, reference, prop_name: String = ""):
	# --- Resource creation from dict: {"type": "RectangleShape2D", "properties": {...}} ---
	if value is Dictionary and value.has("type"):
		return _create_resource(value)

	# --- Resource path: load existing resource ---
	if value is String and (value as String).begins_with("res://"):
		var res = load(value)
		if res != null:
			return res

	# --- Standard type conversions ---
	if reference is Vector2 and value is Array:
		return Vector2(value[0], value[1])
	if reference is Vector3 and value is Array:
		return Vector3(value[0], value[1], value[2])
	if reference is Vector4 and value is Array:
		return Vector4(value[0], value[1], value[2], value[3])
	if reference is Rect2 and value is Array:
		return Rect2(value[0], value[1], value[2], value[3])
	if reference is Color:
		if value is String:
			return Color(value)
		if value is Array:
			return Color(value[0], value[1], value[2], value[3] if value.size() > 3 else 1.0)
	if reference is int:
		return int(value)
	if reference is float:
		return float(value)
	if reference is bool:
		return bool(value)
	if reference is String:
		return str(value)
	if reference is NodePath:
		return NodePath(str(value))

	# --- If reference is null but value is a dict with "type", try resource ---
	if reference == null and value is Dictionary and value.has("type"):
		return _create_resource(value)

	return value


func _create_resource(spec: Dictionary):
	"""Create a Resource from a spec dict: {"type": "ClassName", "properties": {"key": val}}."""
	var type_name: String = spec.get("type", "")
	if type_name.is_empty():
		return null

	if not ClassDB.class_exists(type_name):
		return null

	if not ClassDB.is_parent_class(type_name, "Resource"):
		return null

	var resource = ClassDB.instantiate(type_name)
	if resource == null:
		return null

	var properties: Dictionary = spec.get("properties", {})
	for key in properties:
		var val = properties[key]
		var current = resource.get(key)
		var converted = _convert_value(val, current, key)
		resource.set(key, converted)

	return resource
