class_name MessageQueueManager
extends Node

# TODO Redo this entire system. It's so convoluted and ugly. REFACTOR

@onready var message_queue_state_chart = $MessageQueueStateChart
@onready var message_ready_state = $MessageQueueStateChart/MessageStatus/Ready
@onready var start_delay_timer: Timer = $TTSDelayTimer

const CHATTER = preload("uid://cxjm2cxonifrv")

@onready var chatter_spawn_point_position: Vector2 = get_tree().get_first_node_in_group("chatter_spawn_point").global_position
@onready var chatters_holder := get_tree().get_first_node_in_group("chatters")

@export var core_chat: CoreChat

@onready var message_queue: Array = []
@onready var check_status_timer = $CheckStatusTimer
@onready var active_tts_sphere: Node = null

const START_SPEAKING_DELAY: float= 2.0

@onready var media_loader = get_tree().get_first_node_in_group("media_loader")

enum TTSPhase { IDLE, DELAYING, SPEAKING }
var _phase: int = TTSPhase.IDLE

var _current_utterance_id: int = 0
var _next_utterance_id: int = 1

var _pending_text := ""
var _pending_voice := ""
var _pending_token: int = 0



func _ready():
	check_status_timer.timeout.connect(_check_status)
	core_chat.send_message_to_queue.connect(_add_message_to_queue)

	start_delay_timer.timeout.connect(_on_start_delay_timeout)

	DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_ENDED, Callable(self, "_on_tts_done"))
	DisplayServer.tts_set_utterance_callback(DisplayServer.TTS_UTTERANCE_CANCELED, Callable(self, "_on_tts_done"))


func _add_message_to_queue(message: TwitchChatMessage):
	message_queue.append(message)


func _check_status():
	if message_ready_state.active and not message_queue.is_empty():
		message_queue_state_chart.send_event("go_to_speaking")
		_send_out_next_message(message_queue.pop_front())


func skip_tts_chatter():
	if _phase == TTSPhase.DELAYING:
		if start_delay_timer.time_left > 0.0:
			start_delay_timer.stop()
		_pending_token += 1
		_finish_current()
		return

	if _phase == TTSPhase.SPEAKING:
		DisplayServer.tts_stop()
		return


func _send_out_next_message(message):
	var tts_sphere = CHATTER.instantiate()
	active_tts_sphere = tts_sphere

	tts_sphere.init_chatter_data(str(message["chatter_user_name"]), message["color"])
	tts_sphere.global_position = chatter_spawn_point_position
	chatters_holder.add_child(tts_sphere)

	tts_sphere.set_twitch_message(message, media_loader)
	tts_sphere.update_chatter_display(str(message["chatter_user_name"]), message["color"])
	tts_sphere.soft_body_mesh.stroke_color = message["color"]
	tts_sphere.soft_body_mesh.material = await UserMaterials.get_user_material(message["chatter_user_id"])

	var voice_node = tts_sphere.chatter_voice
	voice_node.init_random_daily_voice(str(message["chatter_user_name"]))
	var filtered_text: String = voice_node.filter_alphanumeric(str(message["message"]["text"]))
	var voice: String = voice_node.random_daily_voice

	if voice_node.request_skip.is_connected(skip_tts_chatter):
		voice_node.request_skip.disconnect(skip_tts_chatter)
	voice_node.request_skip.connect(skip_tts_chatter)
	voice_node.start_fallback()

	if not voice_node.finished_speaking.is_connected(func(): message_queue_state_chart.send_event("go_to_ready")):
		voice_node.finished_speaking.connect(func(): message_queue_state_chart.send_event("go_to_ready"))

	_start_speaking_with_delay(filtered_text, voice, START_SPEAKING_DELAY)


func _start_speaking_with_delay(text: String, voice: String, delay_sec: float):
	_pending_text = text
	_pending_voice = voice

	_pending_token += 1
	var token := _pending_token

	if delay_sec > 0.0:
		_phase = TTSPhase.DELAYING
		start_delay_timer.start(delay_sec)
	else:
		# Speak immediately
		_pending_token = token
		_begin_speaking_now()


func _on_start_delay_timeout():
	if _phase != TTSPhase.DELAYING:
		return
	_begin_speaking_now()


func _begin_speaking_now():
	_phase = TTSPhase.SPEAKING
	_current_utterance_id = _next_utterance_id
	_next_utterance_id += 1

	DisplayServer.tts_speak(_pending_text, _pending_voice, ConfigManager.get_setting("basic", "tts_volume"), 1.0, 1.0, _current_utterance_id, false)


func _on_tts_done(utterance_id: int):
	if _phase != TTSPhase.SPEAKING:
		return
	if utterance_id != _current_utterance_id:
		return

	_finish_current()


func _finish_current():
	_phase = TTSPhase.IDLE
	_current_utterance_id = 0

	if active_tts_sphere and is_instance_valid(active_tts_sphere):
		var voice_node = active_tts_sphere.chatter_voice
		voice_node.stop_fallback()
		voice_node.finished_speaking.emit()
