class_name GECSEditorDebuggerMessages

## A mapping of all the messages sent to the editor debugger.
const Msg = {
	"WORLD_INIT": "gecs:world_init",
	"SYSTEM_METRIC": "gecs:system_metric",
	"SYSTEM_LAST_RUN_DATA": "gecs:system_last_run_data",
	"SET_WORLD": "gecs:set_world",
	"PROCESS_WORLD": "gecs:process_world",
	"EXIT_WORLD": "gecs:exit_world",
	"ENTITY_ADDED": "gecs:entity_added",
	"ENTITY_REMOVED": "gecs:entity_removed",
	"ENTITY_DISABLED": "gecs:entity_disabled",
	"ENTITY_ENABLED": "gecs:entity_enabled",
	"SYSTEM_ADDED": "gecs:system_added",
	"SYSTEM_REMOVED": "gecs:system_removed",
	"ENTITY_COMPONENT_ADDED": "gecs:entity_component_added",
	"ENTITY_COMPONENT_REMOVED": "gecs:entity_component_removed",
	"ENTITY_RELATIONSHIP_ADDED": "gecs:entity_relationship_added",
	"ENTITY_RELATIONSHIP_REMOVED": "gecs:entity_relationship_removed",
	"COMPONENT_PROPERTY_CHANGED": "gecs:component_property_changed",
	"POLL_ENTITY": "gecs:poll_entity",
	"SELECT_ENTITY": "gecs:select_entity",
}


## Helper function to check if we can send messages to the editor debugger.
static func can_send_message() -> bool:
	return not Engine.is_editor_hint() and OS.has_feature("editor")


static func world_init(world: World) -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.WORLD_INIT, [world.get_instance_id(),
				 world.get_path()
			])
	return true

static func system_metric(system: System, time: float) -> bool:
	if can_send_message():
		EngineDebugger.send_message(
			Msg.SYSTEM_METRIC, [system.get_instance_id(),
					 system.name,
					 time
				]
		)
	return true

static func system_last_run_data(system: System, last_run_data: Dictionary) -> bool:
	if can_send_message():
		# Send trimmed data to avoid excessive payload; include execution time and entity count primarily
		EngineDebugger.send_message(
			Msg.SYSTEM_LAST_RUN_DATA,
			[
				system.get_instance_id(),
				system.name,
				last_run_data.duplicate() # duplicate so caller's dictionary isn't mutated
			]
		)
	return true

static func set_world(world: World) -> bool:
	if can_send_message():
		EngineDebugger.send_message(
			Msg.SET_WORLD,
			[world.get_instance_id(),
					 world.get_path()
				]
			if world else []
	)
	return true

static func process_world(delta: float, group_name: String) -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.PROCESS_WORLD, [delta, group_name])
	return true


static func exit_world() -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.EXIT_WORLD, [])
	return true

static func entity_added(ent: Entity) -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.ENTITY_ADDED, [ent.get_instance_id(), ent.get_path()])
	return true

static func entity_removed(ent: Entity) -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.ENTITY_REMOVED, [ent.get_instance_id(), ent.get_path()])
	return true

static func entity_disabled(ent: Entity) -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.ENTITY_DISABLED, [ent.get_instance_id(), ent.get_path()])
	return true

static func entity_enabled(ent: Entity) -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.ENTITY_ENABLED, [ent.get_instance_id(), ent.get_path()])
	return true

static func system_added(sys: System) -> bool:
	if can_send_message():
		EngineDebugger.send_message(
			Msg.SYSTEM_ADDED,
			[
				sys.get_instance_id(),
				sys.group,
				sys.process_empty,
				sys.active,
				sys.paused,
				sys.get_path()
			]
		)
	return true

static func system_removed(sys: System) -> bool:
	if can_send_message():
		EngineDebugger.send_message(Msg.SYSTEM_REMOVED, [sys.get_instance_id(), sys.get_path()])
	return true

static func _get_type_name_for_debugger(obj) -> String:
	if obj == null:
		return "null"
	if obj is Resource or obj is Node:
		var script = obj.get_script()
		if script:
			# Try to get class_name first
			var type_name = script.get_class()
			if type_name and type_name != "GDScript":
				return type_name
			# Otherwise use the resource path (e.g., "res://components/C_Health.gd")
			if script.resource_path:
				return script.resource_path # Returns "C_Health"
		return obj.get_class()
	elif obj is Object:
		return obj.get_class()
	return str(typeof(obj))


static func entity_component_added(ent: Entity, comp: Resource) -> bool:
	if can_send_message():
		EngineDebugger.send_message(
			Msg.ENTITY_COMPONENT_ADDED,
			[
				ent.get_instance_id(),
				comp.get_instance_id(),
				_get_type_name_for_debugger(comp),
				comp.serialize()
			]
		)
	return true

static func entity_component_removed(ent: Entity, comp: Resource) -> bool:
	if can_send_message():
		EngineDebugger.send_message(
			Msg.ENTITY_COMPONENT_REMOVED, [ent.get_instance_id(),
					 comp.get_instance_id()
				]
		)
	return true

static func entity_component_property_changed(
	ent: Entity, comp: Resource, property_name: String, old_value: Variant, new_value: Variant
) -> bool:
	if can_send_message():
		EngineDebugger.send_message(
			Msg.COMPONENT_PROPERTY_CHANGED,
			[ent.get_instance_id(),
					 comp.get_instance_id(),
					 property_name,
					 old_value,
					 new_value
				]
		)
	return true

static func entity_relationship_added(ent: Entity, rel: Relationship) -> bool:
	if can_send_message():
		# Serialize relationship data for debugger display
		var rel_data = {
			"relation_type": _get_type_name_for_debugger(rel.relation) if rel.relation else "null",
			"relation_data": rel.relation.serialize() if rel.relation else {},
			"target_type": "",
			"target_data": {}
		}

		# Format target based on type
		if rel.target == null:
			rel_data["target_type"] = "null"
		elif rel.target is Entity:
			rel_data["target_type"] = "Entity"
			rel_data["target_data"] = {
				"id": rel.target.get_instance_id(),
				"path": str(rel.target.get_path())
			}
		elif rel.target is Component:
			rel_data["target_type"] = "Component"
			rel_data["target_data"] = {
				"type": _get_type_name_for_debugger(rel.target),
				"data": rel.target.serialize()
			}
		elif rel.target is Script:
			rel_data["target_type"] = "Archetype"
			rel_data["target_data"] = {
				"script_path": rel.target.resource_path
			}

		EngineDebugger.send_message(
			Msg.ENTITY_RELATIONSHIP_ADDED,
			[ent.get_instance_id(),
					 rel.get_instance_id(),
					 rel_data
				]
		)
	return true

static func entity_relationship_removed(ent: Entity, rel: Relationship) -> bool:
	if can_send_message():
		EngineDebugger.send_message(
			Msg.ENTITY_RELATIONSHIP_REMOVED, [ent.get_instance_id(),
					 rel.get_instance_id()
				]
		)
	return true
