extends Node

signal quest_registered(quest_id: StringName)
signal quest_started(quest_id: StringName)
signal quest_step_advanced(quest_id: StringName, step_index: int, step_text: String)
signal quest_completed(quest_id: StringName)
signal quest_state_changed

var _quests: Dictionary = {}
var _active_quest_id: StringName = &""
var _active_step_index: int = -1
var _completed_quests: Dictionary = {}


func register_quest(definition: Dictionary) -> bool:
	var raw_id := String(definition.get("id", "")).strip_edges()
	if raw_id.is_empty():
		push_warning("Quest definition is missing an id.")
		return false
	var quest_id: StringName = StringName(raw_id)
	if _quests.has(quest_id):
		return true

	var raw_steps: Variant = definition.get("steps", [])
	if typeof(raw_steps) != TYPE_ARRAY:
		push_warning("Quest '%s' has invalid steps." % raw_id)
		return false
	var normalized_steps: Array[Dictionary] = []
	var idx := 0
	for raw_step in raw_steps:
		if typeof(raw_step) != TYPE_DICTIONARY:
			continue
		var step_dict: Dictionary = raw_step
		var step := step_dict.duplicate(true)
		step["id"] = String(step.get("id", "step_%d" % idx)).strip_edges()
		step["text"] = String(step.get("text", "")).strip_edges()
		step["complete_event"] = StringName(String(step.get("complete_event", "")).strip_edges())
		normalized_steps.append(step)
		idx += 1

	if normalized_steps.is_empty():
		push_warning("Quest '%s' has no usable steps." % raw_id)
		return false

	var quest := definition.duplicate(true)
	quest["id"] = raw_id
	quest["title"] = String(definition.get("title", raw_id))
	quest["description"] = String(definition.get("description", ""))
	quest["steps"] = normalized_steps
	_quests[quest_id] = quest
	quest_registered.emit(quest_id)
	quest_state_changed.emit()
	return true


func has_quest(quest_id: String) -> bool:
	return _quests.has(StringName(quest_id))


func start_quest(quest_id: String) -> bool:
	var id := StringName(quest_id)
	if not _quests.has(id):
		push_warning("Cannot start missing quest '%s'." % quest_id)
		return false
	_active_quest_id = id
	_active_step_index = 0
	quest_started.emit(_active_quest_id)
	var step := _get_active_step()
	if not step.is_empty():
		quest_step_advanced.emit(_active_quest_id, _active_step_index, String(step.get("text", "")))
	quest_state_changed.emit()
	return true


func emit_event(event_name: StringName, payload: Dictionary = {}) -> void:
	if event_name == StringName():
		return
	if _active_quest_id == StringName():
		return
	if is_quest_completed(String(_active_quest_id)):
		return

	var active_step := _get_active_step()
	if active_step.is_empty():
		return
	var expected: StringName = active_step.get("complete_event", StringName())
	if expected != event_name:
		return
	_advance_active_step(payload)


func is_quest_completed(quest_id: String) -> bool:
	return bool(_completed_quests.get(StringName(quest_id), false))


func is_active_quest(quest_id: String) -> bool:
	return _active_quest_id == StringName(quest_id)


func get_active_quest_id() -> StringName:
	return _active_quest_id


func get_active_quest_state() -> Dictionary:
	if _active_quest_id == StringName() or not _quests.has(_active_quest_id):
		return {
			"active": false,
		}

	var quest: Dictionary = _quests[_active_quest_id]
	var steps: Array = quest.get("steps", [])
	var completed: bool = is_quest_completed(String(_active_quest_id))
	var step_text := ""
	var step_id := ""
	if not completed and _active_step_index >= 0 and _active_step_index < steps.size():
		var step: Dictionary = steps[_active_step_index]
		step_text = String(step.get("text", ""))
		step_id = String(step.get("id", ""))

	return {
		"active": true,
		"quest_id": String(_active_quest_id),
		"title": String(quest.get("title", "")),
		"description": String(quest.get("description", "")),
		"current_step_index": _active_step_index,
		"total_steps": steps.size(),
		"current_step_id": step_id,
		"current_step_text": step_text,
		"completed": completed,
	}


func _get_active_step() -> Dictionary:
	if _active_quest_id == StringName():
		return {}
	if not _quests.has(_active_quest_id):
		return {}
	var quest: Dictionary = _quests[_active_quest_id]
	var steps: Array = quest.get("steps", [])
	if _active_step_index < 0 or _active_step_index >= steps.size():
		return {}
	return steps[_active_step_index]


func _advance_active_step(_payload: Dictionary) -> void:
	if _active_quest_id == StringName():
		return
	var quest: Dictionary = _quests.get(_active_quest_id, {})
	var steps: Array = quest.get("steps", [])
	_active_step_index += 1
	if _active_step_index >= steps.size():
		_completed_quests[_active_quest_id] = true
		quest_completed.emit(_active_quest_id)
		quest_state_changed.emit()
		return
	var step: Dictionary = steps[_active_step_index]
	quest_step_advanced.emit(_active_quest_id, _active_step_index, String(step.get("text", "")))
	quest_state_changed.emit()
