@tool
extends Node

## Reads and modifies ProjectSettings and input map.


func register(server: Node) -> void:
	server.register_handler("project_settings", _project_settings)
	server.register_handler("get_input_map", _get_input_map)
	server.register_handler("save_scene", _save_scene)
	server.register_handler("project_context", _project_context)


func _project_settings(args: Dictionary) -> Dictionary:
	var action: String = args.get("action", "get")
	var key: String = args.get("key", "")

	if action == "list":
		# Return commonly useful settings
		var settings := [
			"application/config/name",
			"application/config/description",
			"application/run/main_scene",
			"display/window/size/viewport_width",
			"display/window/size/viewport_height",
			"display/window/stretch/mode",
			"display/window/stretch/aspect",
			"rendering/renderer/rendering_method",
			"physics/2d/default_gravity",
			"physics/3d/default_gravity",
		]
		var lines: PackedStringArray = []
		for s in settings:
			if ProjectSettings.has_setting(s):
				lines.append("%s = %s" % [s, str(ProjectSettings.get_setting(s))])
		return {"ok": true, "type": "text", "result": "\n".join(lines)}

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

	if action == "get":
		if not ProjectSettings.has_setting(key):
			return {"ok": false, "error": "Setting not found: %s" % key}
		var value = ProjectSettings.get_setting(key)
		return {"ok": true, "type": "text", "result": "%s = %s" % [key, str(value)]}

	if action == "set":
		var value = args.get("value")
		if value == null:
			return {"ok": false, "error": "Missing 'value' argument for set"}
		ProjectSettings.set_setting(key, value)
		ProjectSettings.save()
		return {"ok": true, "type": "text", "result": "Set %s = %s" % [key, str(value)]}

	return {"ok": false, "error": "Unknown action: %s (use 'get', 'set', or 'list')" % action}


func _get_input_map(_args: Dictionary) -> Dictionary:
	var actions := InputMap.get_actions()
	var lines: PackedStringArray = []

	for action in actions:
		# Skip built-in UI actions
		if str(action).begins_with("ui_"):
			continue
		var events := InputMap.action_get_events(action)
		var event_strs: PackedStringArray = []
		for event in events:
			event_strs.append(event.as_text())
		if event_strs.size() > 0:
			lines.append("%s: %s" % [action, ", ".join(event_strs)])
		else:
			lines.append("%s: (no bindings)" % action)

	if lines.size() == 0:
		return {"ok": true, "type": "text", "result": "No custom input actions defined."}

	return {"ok": true, "type": "text", "result": "\n".join(lines)}


func _save_scene(_args: Dictionary) -> Dictionary:
	var root := EditorInterface.get_edited_scene_root()
	if root == null:
		return {"ok": false, "error": "No scene open"}

	var scene_path := root.scene_file_path
	if scene_path.is_empty():
		return {"ok": false, "error": "Scene has no file path — save it manually first"}

	EditorInterface.save_scene()
	return {"ok": true, "type": "text", "result": "Scene saved: %s" % scene_path}


# ------------------------------------------------------------------
# Project context dump
# ------------------------------------------------------------------

func _project_context(_args: Dictionary) -> Dictionary:
	var lines: PackedStringArray = []

	# -- Project info --
	lines.append("== Project Info ==")
	var info_keys := [
		"application/config/name",
		"application/run/main_scene",
		"display/window/size/viewport_width",
		"display/window/size/viewport_height",
		"rendering/renderer/rendering_method",
	]
	for key in info_keys:
		if ProjectSettings.has_setting(key):
			lines.append("  %s = %s" % [key.get_file(), str(ProjectSettings.get_setting(key))])

	# -- Autoloads --
	var autoloads: PackedStringArray = []
	for prop in ProjectSettings.get_property_list():
		var pname: String = prop["name"]
		if pname.begins_with("autoload/"):
			var al_name := pname.substr(9)
			var al_path := str(ProjectSettings.get_setting(pname)).lstrip("*")
			autoloads.append("  %s -> %s" % [al_name, al_path])
	lines.append("")
	lines.append("== Autoloads (%d) ==" % autoloads.size())
	lines.append_array(autoloads)

	# -- Input map (skip ui_*) --
	var input_lines: PackedStringArray = []
	for action in InputMap.get_actions():
		if str(action).begins_with("ui_"):
			continue
		var events := InputMap.action_get_events(action)
		var event_strs: PackedStringArray = []
		for event in events:
			event_strs.append(event.as_text())
		if event_strs.size() > 0:
			input_lines.append("  %s: %s" % [action, ", ".join(event_strs)])
		else:
			input_lines.append("  %s: (no bindings)" % action)
	lines.append("")
	lines.append("== Input Map (%d actions) ==" % input_lines.size())
	lines.append_array(input_lines)

	# -- File scan --
	var scripts: PackedStringArray = []
	var scenes: PackedStringArray = []
	var resources: PackedStringArray = []
	_scan_project_files("res://", scripts, scenes, resources)

	lines.append("")
	lines.append("== Scripts (%d) ==" % scripts.size())
	for s in scripts:
		lines.append("  %s" % s)

	lines.append("")
	lines.append("== Scenes (%d) ==" % scenes.size())
	for s in scenes:
		lines.append("  %s" % s)

	if resources.size() > 0:
		lines.append("")
		lines.append("== Resources (%d) ==" % resources.size())
		for r in resources:
			lines.append("  %s" % r)

	return {"ok": true, "type": "text", "result": "\n".join(lines)}


func _scan_project_files(path: String, scripts: PackedStringArray, scenes: PackedStringArray, resources: PackedStringArray) -> void:
	var dir := DirAccess.open(path)
	if dir == null:
		return
	dir.list_dir_begin()
	var file_name := dir.get_next()
	while file_name != "":
		var full_path: String = path.path_join(file_name)
		if dir.current_is_dir():
			if not file_name.begins_with(".") and file_name != "addons":
				_scan_project_files(full_path, scripts, scenes, resources)
		else:
			if file_name.ends_with(".gd"):
				scripts.append(full_path)
			elif file_name.ends_with(".tscn"):
				scenes.append(full_path)
			elif file_name.ends_with(".tres"):
				resources.append(full_path)
		file_name = dir.get_next()
	dir.list_dir_end()
