class_name JoinGamePage
extends Control

signal join_requested(endpoint: String)
signal back_requested

enum Mode {
	DISCOVER,
	DIRECT,
	SAVED,
	RECENT,
}

const DISCOVERY_REFRESH_SECONDS: float = 8.0

const ADDRESS_FORMAT_HELP: String = (
	"Hostname, IPv4, or [IPv6]. Port 7777 is used when omitted; "
	+ "include the port shown by a host when it differs."
)
const DIRECT_WORKFLOW_HELP: String = (
	"%s\nJoin Now connects once; Save Server stores this address locally."
	% ADDRESS_FORMAT_HELP
)

@onready var _address: LineEdit = %Address
@onready var _address_label: Label = %AddressLabel
@onready var _address_helper: Label = %AddressHelper
@onready var _name_edit: LineEdit = %NameEdit
@onready var _name_label: Label = %NameLabel
@onready var _name_helper: Label = %NameHelper
@onready var _server_list: ItemList = %ServerList
@onready var _details: Label = %Details
@onready var _discover_button: Button = %DiscoverButton
@onready var _direct_button: Button = %DirectButton
@onready var _saved_button: Button = %SavedButton
@onready var _recent_button: Button = %RecentButton
@onready var _join_button: Button = %JoinButton
@onready var _refresh_button: Button = %RefreshButton
@onready var _save_button: Button = %SaveButton
@onready var _edit_button: Button = %EditButton
@onready var _favorite_button: Button = %FavoriteButton
@onready var _delete_button: Button = %DeleteButton
@onready var _cancel_button: Button = %CancelButton
@onready var _back_button: Button = %BackButton
@onready var _open_close_button: Button = %OpenCloseButton
@onready var _status: Label = %Status
@onready var _session_summary: Label = %SessionSummary
@onready var _delete_confirmation: BubbleConfirmationPage = (
	%DeleteConfirmation
)

var _network_session: NetworkSession
var _saved_servers: SavedServerStore
var _server_trust: ServerTrustStore
var _discovery: DiscoveryClient
var _gameplay_context: bool = false
var _mode: Mode = Mode.DISCOVER
var _visible_entries: Array[SavedServerEntry] = []
var _selected_entry: SavedServerEntry
var _discovery_rooms: Array[Dictionary] = []
var _selected_discovery_index: int = -1
var _discovery_refresh_timer: Timer
var _editing_entry_id: String = ""
var _name_entry_active: bool = false
var _delete_armed: bool = false


func _ready() -> void:
	UtilityPageStyle.apply_page(self)
	var paper := get_node("Paper") as PanelContainer
	paper.add_theme_stylebox_override(
		"panel", UtilityPageStyle.panel_style()
	)
	for button: BaseButton in [
		_discover_button, _direct_button, _saved_button, _recent_button,
		_refresh_button, _join_button,
		_save_button, _edit_button, _favorite_button, _delete_button,
		_cancel_button, _back_button, _open_close_button,
	]:
		UtilityPageStyle.apply_ocean_button(button)
	UtilityPageStyle.apply_ocean_line_edit(_address)
	UtilityPageStyle.apply_ocean_line_edit(_name_edit)
	_discover_button.pressed.connect(_set_mode.bind(Mode.DISCOVER))
	_direct_button.pressed.connect(_set_mode.bind(Mode.DIRECT))
	_saved_button.pressed.connect(_set_mode.bind(Mode.SAVED))
	_recent_button.pressed.connect(_set_mode.bind(Mode.RECENT))
	_refresh_button.pressed.connect(_request_discovery_refresh)
	_join_button.pressed.connect(_request_join)
	_save_button.pressed.connect(_on_save_pressed)
	_edit_button.pressed.connect(_on_edit_pressed)
	_favorite_button.pressed.connect(_on_favorite_pressed)
	_delete_button.pressed.connect(_on_delete_pressed)
	_cancel_button.pressed.connect(_on_cancel_pressed)
	_back_button.pressed.connect(_on_back_pressed)
	_open_close_button.pressed.connect(_on_open_close_pressed)
	_address.text_submitted.connect(func(_value: String) -> void:
		if _name_entry_active:
			_name_edit.grab_focus()
			_name_edit.select_all()
		else:
			_request_join()
	)
	_name_edit.text_submitted.connect(func(_value: String) -> void:
		_commit_name_entry()
	)
	_server_list.item_selected.connect(_on_list_item_selected)
	_server_list.item_activated.connect(func(_index: int) -> void:
		_request_join()
	)
	_delete_confirmation.confirmed.connect(_confirm_delete)
	_delete_confirmation.cancelled.connect(_cancel_delete)
	_discovery_refresh_timer = Timer.new()
	_discovery_refresh_timer.wait_time = DISCOVERY_REFRESH_SECONDS
	_discovery_refresh_timer.one_shot = false
	_discovery_refresh_timer.timeout.connect(_request_discovery_refresh)
	add_child(_discovery_refresh_timer)
	hide()


func setup(
	network_session: NetworkSession,
	saved_servers: SavedServerStore,
	gameplay_context: bool,
	server_trust: ServerTrustStore = null,
	discovery: DiscoveryClient = null,
) -> void:
	_network_session = network_session
	_saved_servers = saved_servers
	_gameplay_context = gameplay_context
	_server_trust = server_trust
	_discovery = discovery
	if not _network_session.state_changed.is_connected(_on_state_changed):
		_network_session.state_changed.connect(_on_state_changed)
	if not _network_session.status_message_changed.is_connected(
		_on_status_message_changed
	):
		_network_session.status_message_changed.connect(
			_on_status_message_changed
		)
	if not _network_session.connection_error.is_connected(
		_on_connection_error
	):
		_network_session.connection_error.connect(_on_connection_error)
	if not _network_session.peer_count_changed.is_connected(
		_on_peer_count_changed
	):
		_network_session.peer_count_changed.connect(_on_peer_count_changed)
	if (
		_saved_servers != null
		and not _saved_servers.data_changed.is_connected(_on_store_changed)
	):
		_saved_servers.data_changed.connect(_on_store_changed)
	if _discovery != null:
		if not _discovery.rooms_updated.is_connected(
			_on_discovery_rooms_updated
		):
			_discovery.rooms_updated.connect(_on_discovery_rooms_updated)
		if not _discovery.browse_status_changed.is_connected(
			_on_discovery_status_changed
		):
			_discovery.browse_status_changed.connect(
				_on_discovery_status_changed
			)
		if not _discovery.public_join_prepared.is_connected(
			_on_public_join_prepared
		):
			_discovery.public_join_prepared.connect(
				_on_public_join_prepared
			)
		if not _discovery.public_join_status_changed.is_connected(
			_on_public_join_status_changed
		):
			_discovery.public_join_status_changed.connect(
				_on_public_join_status_changed
			)
	_refresh()


func open_page(preserved_endpoint: String = "") -> void:
	if not preserved_endpoint.is_empty():
		_address.text = preserved_endpoint
	elif _address.text.is_empty():
		_address.text = "127.0.0.1:7777"
	show()
	UtilityPageStyle.animate_in(self)
	_set_mode(_mode)
	if _mode == Mode.DIRECT:
		_address.grab_focus()
		_address.select_all()


func close_page() -> void:
	_clear_edit_state()
	_discovery_refresh_timer.stop()
	hide()
	var current_viewport: Viewport = get_viewport()
	if current_viewport != null:
		current_viewport.gui_release_focus()


func get_endpoint_text() -> String:
	return _address.text


func set_status(message: String) -> void:
	_set_status(_friendly_connection_message(message), true)


func _set_mode(mode: Mode) -> void:
	_mode = mode
	_selected_entry = null
	_selected_discovery_index = -1
	_clear_edit_state()
	_refresh_entries()
	_refresh()
	if mode == Mode.DISCOVER:
		_discovery_refresh_timer.start()
		_request_discovery_refresh()
	else:
		_discovery_refresh_timer.stop()
	if not is_visible_in_tree():
		return
	if mode == Mode.DIRECT:
		_address.grab_focus()
	else:
		_server_list.grab_focus()


func _request_join() -> void:
	if _network_session.state in [
		NetworkSession.State.CONNECTION_FAILED,
		NetworkSession.State.SERVER_LOST,
	]:
		_network_session.reset_failure()
	var endpoint_text: String = _address.text
	if _mode == Mode.DISCOVER:
		var room: Dictionary = _selected_discovery_room()
		if _discovery_room_is_full(room):
			_set_status("That room is full.", true)
			return
		if _discovery == null or not _discovery.prepare_public_join(room):
			_set_status("Could not prepare the public connection.", true)
		return
	elif _mode != Mode.DIRECT and _selected_entry != null:
		endpoint_text = _selected_entry.normalized_endpoint
	var endpoint: ConnectionEndpoint = EndpointParser.parse(endpoint_text)
	if not endpoint.is_valid():
		_set_status(endpoint.error_message, true)
		return
	_set_status("Connecting…")
	_address.text = endpoint.normalized_display
	join_requested.emit(endpoint.normalized_display)


func _on_public_join_prepared(endpoint_text: String) -> void:
	if _mode != Mode.DISCOVER or not is_visible_in_tree():
		return
	var endpoint: ConnectionEndpoint = EndpointParser.parse(endpoint_text)
	if not endpoint.is_valid():
		_set_status("Discovery returned an invalid room address.", true)
		return
	_address.text = endpoint.normalized_display
	_set_status("Connecting…")
	join_requested.emit(endpoint.normalized_display)


func _on_public_join_status_changed(message: String, is_error: bool) -> void:
	if _mode == Mode.DISCOVER and is_visible_in_tree():
		_set_status(message, is_error)
		_refresh()


func _on_save_pressed() -> void:
	if _name_entry_active:
		_commit_name_entry()
		return
	var endpoint: ConnectionEndpoint = (
		_selected_entry.get_endpoint()
		if _mode == Mode.RECENT and _selected_entry != null
		else EndpointParser.parse(_address.text)
	)
	if not endpoint.is_valid():
		_set_status(endpoint.error_message, true)
		return
	var existing: SavedServerEntry = (
		_saved_servers.find_saved_by_endpoint(endpoint)
	)
	if existing != null:
		_set_status(
			"Already saved locally as “%s”." % existing.display_name,
			true,
		)
		_set_mode(Mode.SAVED)
		_select_entry_id(existing.entry_id)
		return
	_address.text = endpoint.normalized_display
	_name_edit.text = (
		_selected_entry.last_observed_server_name
		if _mode == Mode.RECENT
		and _selected_entry != null
		and not _selected_entry.last_observed_server_name.is_empty()
		else endpoint.host
	)
	_name_entry_active = true
	_name_edit.show()
	_save_button.text = "save"
	_name_edit.grab_focus()
	_name_edit.select_all()
	_refresh()


func _commit_name_entry() -> void:
	var endpoint := EndpointParser.parse(_address.text)
	if not endpoint.is_valid():
		_set_status(endpoint.error_message, true)
		return
	var saved: SavedServerEntry = _saved_servers.save_or_update_entry(
		_name_edit.text,
		endpoint,
		_editing_entry_id,
	)
	if saved == null:
		_set_status(
			"Could not save. Check the server name and address.",
			true,
		)
		return
	var was_editing: bool = not _editing_entry_id.is_empty()
	_set_status(
		"Server updated."
		if was_editing
		else "Saved locally as “%s”." % saved.display_name
	)
	_clear_edit_state()
	_mode = Mode.SAVED
	_refresh_entries()
	_select_entry_id(saved.entry_id)
	_refresh()


func _on_edit_pressed() -> void:
	if _selected_entry == null or _mode != Mode.SAVED:
		return
	_editing_entry_id = _selected_entry.entry_id
	_address.text = _selected_entry.normalized_endpoint
	_name_edit.text = _selected_entry.display_name
	_name_entry_active = true
	_name_edit.show()
	_save_button.text = "save"
	_name_edit.grab_focus()
	_name_edit.select_all()
	_refresh()


func _on_favorite_pressed() -> void:
	if _selected_entry == null or _mode != Mode.SAVED:
		return
	var entry_id: String = _selected_entry.entry_id
	_saved_servers.set_favorite(entry_id, not _selected_entry.favorite)
	_refresh_entries()
	_select_entry_id(entry_id)
	_refresh()


func _on_delete_pressed() -> void:
	if _selected_entry == null or _mode == Mode.DIRECT:
		return
	_delete_armed = true
	_delete_confirmation.configure(
		(
			"Remove this recent connection from this device?"
			if _mode == Mode.RECENT
			else "Delete this saved-server bookmark from this device?"
		),
		"remove" if _mode == Mode.RECENT else "delete",
		"cancel",
		BubbleConfirmationPage.InitialFocus.CANCEL,
	)
	_delete_confirmation.transition_in(0.18, func() -> void: pass)


func _confirm_delete() -> void:
	if _selected_entry == null or not _delete_armed:
		_cancel_delete()
		return
	var removed: bool = (
		_saved_servers.remove_recent_entry(_selected_entry.entry_id)
		if _mode == Mode.RECENT
		else _saved_servers.remove_saved_entry(_selected_entry.entry_id)
	)
	_set_status(
		(
			"Removed from recent connections."
			if _mode == Mode.RECENT
			else "Removed from saved servers."
		)
		if removed
		else "Could not remove the local entry.",
		not removed,
	)
	_selected_entry = null
	_delete_armed = false
	_refresh_entries()
	_refresh()
	_delete_confirmation.hide_page()


func _cancel_delete() -> void:
	_delete_armed = false
	_delete_confirmation.hide_page()
	_delete_button.grab_focus()


func _on_list_item_selected(index: int) -> void:
	if _mode == Mode.DISCOVER:
		if index < 0 or index >= _discovery_rooms.size():
			return
		_selected_discovery_index = index
		_selected_entry = null
		_refresh()
		return
	if index < 0 or index >= _visible_entries.size():
		return
	_selected_entry = _visible_entries[index]
	_delete_armed = false
	_refresh()


func _select_entry_id(entry_id: String) -> void:
	for index: int in range(_visible_entries.size()):
		if _visible_entries[index].entry_id == entry_id:
			_server_list.select(index)
			_selected_entry = _visible_entries[index]
			return


func _refresh_entries() -> void:
	_visible_entries.clear()
	_server_list.clear()
	if _mode == Mode.DISCOVER:
		for room: Dictionary in _discovery_rooms:
			var player_count: int = int(room.get("current_players", 0))
			var maximum: int = int(room.get("max_players", 0))
			_server_list.add_item(
				"%s  —  %d / %d players%s" % [
					str(room.get("room_name", "Public room")),
					player_count,
					maximum,
					"  —  full" if player_count >= maximum else "",
				]
			)
		return
	if _saved_servers == null or _mode == Mode.DIRECT:
		return
	_visible_entries = (
		_saved_servers.get_saved_entries()
		if _mode == Mode.SAVED
		else _saved_servers.get_recent_entries()
	)
	for entry: SavedServerEntry in _visible_entries:
		var prefix: String = "★ " if entry.favorite else ""
		var count: String = (
			"  —  %d / %d players"
			% [
				entry.last_observed_player_count,
				entry.last_observed_max_players,
			]
			if entry.last_observed_max_players > 0
			else ""
		)
		_server_list.add_item(
			"%s%s  —  %s%s"
			% [
				prefix,
				entry.display_name,
				entry.normalized_endpoint,
				count,
			]
		)


func _refresh() -> void:
	if not is_node_ready() or _network_session == null:
		return
	var connecting: bool = _network_session.state in [
		NetworkSession.State.CONNECTING,
		NetworkSession.State.AUTHENTICATING,
	] or (_discovery != null and _discovery.is_public_join_preparing())
	var direct: bool = _mode == Mode.DIRECT
	var discovery_mode: bool = _mode == Mode.DISCOVER
	var selected: bool = (
		_selected_discovery_index >= 0
		if discovery_mode
		else _selected_entry != null
	)
	_discover_button.button_pressed = discovery_mode
	_direct_button.button_pressed = direct
	_saved_button.button_pressed = _mode == Mode.SAVED
	_recent_button.button_pressed = _mode == Mode.RECENT
	_address.visible = direct or _name_entry_active
	_address_label.visible = _address.visible
	_address_helper.visible = _address.visible
	_address_helper.text = (
		ADDRESS_FORMAT_HELP if _name_entry_active else DIRECT_WORKFLOW_HELP
	)
	_address.editable = not connecting
	_name_edit.visible = _name_entry_active
	_name_label.visible = _name_entry_active
	_name_helper.visible = _name_entry_active
	_server_list.visible = not direct and not _name_entry_active
	_details.visible = not direct and not _name_entry_active
	_join_button.disabled = (
		connecting
		or (not direct and not selected)
		or (
			discovery_mode
			and selected
			and _discovery_room_is_full(_selected_discovery_room())
		)
	)
	_join_button.text = "join\nnow" if direct else "join"
	_refresh_button.visible = discovery_mode and not _name_entry_active
	_save_button.visible = (
		direct or _mode == Mode.RECENT or _name_entry_active
	)
	_save_button.disabled = connecting or (
		_mode == Mode.RECENT and not selected and not _name_entry_active
	)
	_save_button.text = "save" if _name_entry_active else "save\nserver"
	_edit_button.visible = _mode == Mode.SAVED and selected
	_favorite_button.visible = _mode == Mode.SAVED and selected
	_favorite_button.text = (
		"unfavorite"
		if _mode == Mode.SAVED
		and _selected_entry != null
		and _selected_entry.favorite
		else "favorite"
	)
	_delete_button.visible = (
		_mode in [Mode.SAVED, Mode.RECENT] and selected
	)
	_delete_button.text = "remove" if _mode == Mode.RECENT else "delete"
	_cancel_button.visible = connecting
	_open_close_button.visible = (
		_gameplay_context and _network_session.is_host()
	)
	if _open_close_button.visible:
		_open_close_button.text = (
			"close\ngame"
			if _network_session.is_open_host()
			else "open\ngame"
		)
	_session_summary.visible = (
		_gameplay_context
		and _network_session.state != NetworkSession.State.INACTIVE
	)
	if _session_summary.visible:
		_session_summary.text = (
			"UDP %d • %d / %d players"
			% [
				_network_session.get_host_port(),
				_network_session.get_player_count(),
				_network_session.get_session_max_players(),
			]
			if _network_session.is_host()
			else "%d / %d players" % [
				_network_session.get_player_count(),
				_network_session.get_session_max_players(),
			]
		)
	if not direct:
		if selected:
			_details.text = (
				_format_discovery_details(_selected_discovery_room())
				if discovery_mode
				else _format_entry_details(_selected_entry)
			)
		elif (
			_discovery_rooms.is_empty()
			if discovery_mode
			else _visible_entries.is_empty()
		):
			_details.text = (
				"No public rooms are available."
				if discovery_mode
				else "No saved servers yet."
				if _mode == Mode.SAVED
				else "No recent connections yet."
			)
		else:
			_details.text = "Select a server."
	var warning: String = (
		_saved_servers.get_recovery_warning()
		if _saved_servers != null and not discovery_mode
		else ""
	)
	if not warning.is_empty() and _status.text.is_empty():
		_status.text = warning


func _format_entry_details(entry: SavedServerEntry) -> String:
	var parts: Array[String] = []
	if _mode == Mode.SAVED:
		parts.append("Name: %s" % entry.display_name)
	parts.append("Address: %s" % entry.normalized_endpoint)
	if _server_trust != null:
		var trust := _server_trust.get_record(entry.get_endpoint())
		if trust.is_empty():
			parts.append("Host identity: Not verified yet")
		else:
			parts.append(
				"Host identity: %s"
				% NetworkIdentityCrypto.format_fingerprint(
					str(trust.get("fingerprint", ""))
				)
			)
	if (
		not entry.last_observed_server_name.is_empty()
		and (
			_mode != Mode.SAVED
			or entry.last_observed_server_name != entry.display_name
		)
	):
		parts.append("Server name: %s" % entry.last_observed_server_name)
	if entry.last_observed_max_players > 0:
		parts.append(
			"Players: %d / %d"
			% [
				entry.last_observed_player_count,
				entry.last_observed_max_players,
			]
		)
	if entry.last_success_at_unix > 0:
		parts.append(
			"Last connected: %s"
			% Time.get_datetime_string_from_unix_time(
				entry.last_success_at_unix, true
			)
		)
	if not entry.last_result_code.is_empty():
		parts.append(
			"Last result: %s" % _format_result_code(entry.last_result_code)
		)
	return "\n".join(parts)


func _format_discovery_details(room: Dictionary) -> String:
	if room.is_empty():
		return "Select a public room."
	return "\n".join([
		"Room: %s" % str(room.get("room_name", "Public room")),
		"Players: %d / %d" % [
			int(room.get("current_players", 0)),
			int(room.get("max_players", 0)),
		],
		"Connection: %s" % (
			_discovery.room_endpoint(room) if _discovery != null else "—"
		),
		"Direct UDP connection • ping is measured after joining",
	])


func _selected_discovery_room() -> Dictionary:
	if (
		_selected_discovery_index < 0
		or _selected_discovery_index >= _discovery_rooms.size()
	):
		return {}
	return _discovery_rooms[_selected_discovery_index]


func _discovery_room_is_full(room: Dictionary) -> bool:
	if room.is_empty():
		return false
	return int(room.get("current_players", 0)) >= int(room.get("max_players", 0))


func _request_discovery_refresh() -> void:
	if (
		_discovery == null
		or _mode != Mode.DISCOVER
		or not is_visible_in_tree()
	):
		return
	_discovery.request_rooms()


func _on_discovery_rooms_updated(rooms: Array[Dictionary]) -> void:
	var selected_id: String = str(
		_selected_discovery_room().get("room_id", "")
	)
	_discovery_rooms = rooms.duplicate(true)
	_selected_discovery_index = -1
	if _mode == Mode.DISCOVER:
		_refresh_entries()
		if not selected_id.is_empty():
			for index: int in _discovery_rooms.size():
				if str(_discovery_rooms[index].get("room_id", "")) == selected_id:
					_selected_discovery_index = index
					_server_list.select(index)
					break
		_refresh()


func _on_discovery_status_changed(message: String, is_error: bool) -> void:
	if _mode == Mode.DISCOVER and is_visible_in_tree():
		_set_status(message, is_error)


func _format_result_code(result_code: String) -> String:
	match result_code.strip_edges().to_upper():
		"SUCCESS":
			return "Connected successfully"
		"PROTOCOL_MISMATCH":
			return "Protocol versions did not match"
		"SERVER_FULL":
			return "Server was full"
		"CANCELLED":
			return "Connection cancelled"
		"TIMEOUT", "CONNECTION_FAILED", "SERVER_UNAVAILABLE":
			return "Could not reach the server"
		_:
			return "Connection was unsuccessful"


func _friendly_connection_message(message: String) -> String:
	var normalized: String = message.strip_edges().to_lower()
	if "protocol" in normalized and (
		"mismatch" in normalized or "version" in normalized
	):
		return "Protocol versions do not match."
	if "full" in normalized or "capacity" in normalized:
		return "Server is full."
	if "cancel" in normalized:
		return "Connection cancelled."
	if (
		"timeout" in normalized
		or "unavailable" in normalized
		or "refused" in normalized
		or "reach" in normalized
		or "failed" in normalized
	):
		return "Could not reach the server."
	return message


func _set_status(message: String, is_error: bool = false) -> void:
	_status.text = message
	_status.add_theme_color_override(
		"font_color",
		UtilityPageStyle.OCEAN_DANGER
		if is_error
		else UtilityPageStyle.OCEAN_TEXT_PRIMARY,
	)


func _clear_edit_state() -> void:
	_editing_entry_id = ""
	_name_entry_active = false
	_delete_armed = false
	if is_node_ready():
		_name_edit.hide()
		_delete_confirmation.hide_page()


func _on_cancel_pressed() -> void:
	if _network_session != null:
		_network_session.cancel_connection()
	_refresh()


func _on_back_pressed() -> void:
	if _name_entry_active or _delete_armed:
		_clear_edit_state()
		_refresh()
		return
	if (
		_network_session != null
		and _network_session.state in [
			NetworkSession.State.CONNECTING,
			NetworkSession.State.AUTHENTICATING,
		]
	):
		_network_session.cancel_connection()
	back_requested.emit()


func _on_open_close_pressed() -> void:
	if _network_session == null or not _network_session.is_host():
		return
	_network_session.set_host_open(not _network_session.is_open_host())
	_refresh()


func _on_state_changed(_state: NetworkSession.State) -> void:
	_refresh()


func _on_status_message_changed(message: String) -> void:
	_set_status(_friendly_connection_message(message))
	_refresh()


func _on_connection_error(message: String) -> void:
	_set_status(_friendly_connection_message(message), true)
	_refresh()


func _on_peer_count_changed(player_count: int, max_players: int) -> void:
	_session_summary.text = "%d / %d players" % [
		player_count,
		max_players,
	]


func _on_store_changed() -> void:
	var selected_id: String = (
		_selected_entry.entry_id if _selected_entry != null else ""
	)
	_refresh_entries()
	if not selected_id.is_empty():
		_select_entry_id(selected_id)
	_refresh()
