@tool
extends Window

const FUZZY_SEARCH = preload("res://addons/transition_configurator/fuzzy_search.gd")
const TRANSITION_PIECE_SCENE = "res://TransitionPiece.tscn"

var transition_piece: TransitionPiece = null

# UI elements
@onready var search_input: LineEdit
@onready var room_list: ItemList
@onready var room_filter_label: Label
@onready var arrival_list: ItemList
@onready var arrival_label: Label
@onready var status_label: Label
@onready var apply_button: Button

# Data
var all_rooms: Array[Dictionary] = []
var current_room_path: String = ""

func _ready() -> void:
	title = "Configure Transition Exit"
	min_size = Vector2i(900, 600)
	
	var main_vbox = VBoxContainer.new()
	main_vbox.set_anchors_preset(Control.PRESET_FULL_RECT)
	main_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	main_vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
	main_vbox.add_theme_constant_override("separation", 10)
	add_child(main_vbox)
	
	# Add margins
	var margin = MarginContainer.new()
	margin.add_theme_constant_override("margin_left", 20)
	margin.add_theme_constant_override("margin_right", 20)
	margin.add_theme_constant_override("margin_top", 20)
	margin.add_theme_constant_override("margin_bottom", 20)
	margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	margin.size_flags_vertical = Control.SIZE_EXPAND_FILL
	main_vbox.add_child(margin)
	
	var content_vbox = VBoxContainer.new()
	content_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	content_vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
	content_vbox.add_theme_constant_override("separation", 15)
	margin.add_child(content_vbox)
	
	# Title
	var title_label = Label.new()
	title_label.text = "Configure Exit for: %s" % (transition_piece.name if transition_piece else "Unknown")
	title_label.add_theme_font_size_override("font_size", 18)
	content_vbox.add_child(title_label)
	
	# Main content - HSplitContainer
	var split = HSplitContainer.new()
	split.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	split.size_flags_vertical = Control.SIZE_EXPAND_FILL
	split.split_offset = 300
	content_vbox.add_child(split)
	
	# Left side - Room selection
	var left_vbox = VBoxContainer.new()
	left_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	left_vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
	left_vbox.add_theme_constant_override("separation", 10)
	split.add_child(left_vbox)
	
	var search_label = Label.new()
	search_label.text = "Search Rooms:"
	left_vbox.add_child(search_label)
	
	search_input = LineEdit.new()
	search_input.placeholder_text = "Type to filter rooms..."
	search_input.text_changed.connect(_on_search_changed)
	left_vbox.add_child(search_input)
	
	room_filter_label = Label.new()
	room_filter_label.text = "Showing all rooms"
	left_vbox.add_child(room_filter_label)
	
	room_list = ItemList.new()
	room_list.size_flags_vertical = Control.SIZE_EXPAND_FILL
	room_list.item_selected.connect(_on_room_selected)
	left_vbox.add_child(room_list)
	
	# Right side - Arrival point selection
	var right_vbox = VBoxContainer.new()
	right_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	right_vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
	right_vbox.add_theme_constant_override("separation", 10)
	split.add_child(right_vbox)
	
	arrival_label = Label.new()
	arrival_label.text = "Select a room to see arrival points"
	arrival_label.add_theme_font_size_override("font_size", 14)
	right_vbox.add_child(arrival_label)
	
	arrival_list = ItemList.new()
	arrival_list.size_flags_vertical = Control.SIZE_EXPAND_FILL
	arrival_list.item_selected.connect(func(idx): _validate_selection())
	right_vbox.add_child(arrival_list)
	
	# Status label
	status_label = Label.new()
	status_label.text = ""
	content_vbox.add_child(status_label)
	
	# Buttons
	var button_hbox = HBoxContainer.new()
	button_hbox.alignment = BoxContainer.ALIGNMENT_END
	button_hbox.add_theme_constant_override("separation", 10)
	content_vbox.add_child(button_hbox)
	
	apply_button = Button.new()
	apply_button.text = "Apply Changes"
	apply_button.disabled = true
	apply_button.custom_minimum_size = Vector2(150, 40)
	apply_button.pressed.connect(_on_apply)
	button_hbox.add_child(apply_button)
	
	var cancel_button = Button.new()
	cancel_button.text = "Cancel"
	cancel_button.custom_minimum_size = Vector2(100, 40)
	cancel_button.pressed.connect(hide)
	button_hbox.add_child(cancel_button)
	
	# Close on escape
	close_requested.connect(hide)

func populate_rooms() -> void:
	all_rooms.clear()
	room_list.clear()
	
	var scene_files = _find_room_scenes()
	for file_path in scene_files:
		var room_name = file_path.get_file().get_basename()
		var uid = _get_scene_uid(file_path)
		all_rooms.append({
			"name": room_name,
			"path": file_path,
			"uid": uid
		})
	
	# Sort by name
	all_rooms.sort_custom(func(a, b): return a["name"] < b["name"])
	
	_update_room_list(all_rooms)
	
	room_filter_label.text = "Showing %d rooms" % all_rooms.size()

func _update_room_list(rooms: Array) -> void:
	room_list.clear()
	for room in rooms:
		room_list.add_item(room["name"])
		room_list.set_item_metadata(room_list.item_count - 1, room)

func _on_search_changed(new_text: String) -> void:
	if new_text.is_empty():
		_update_room_list(all_rooms)
		room_filter_label.text = "Showing all %d rooms" % all_rooms.size()
	else:
		var filtered = FUZZY_SEARCH.sort_by_match(new_text, all_rooms, "name")
		_update_room_list(filtered)
		room_filter_label.text = "Showing %d of %d rooms" % [filtered.size(), all_rooms.size()]

func _on_room_selected(index: int) -> void:
	arrival_list.clear()
	apply_button.disabled = true
	
	if index < 0 or index >= room_list.item_count:
		return
	
	var room_data = room_list.get_item_metadata(index)
	current_room_path = room_data["path"]
	
	arrival_label.text = "Arrival Points in %s:" % room_data["name"]
	
	# Find all TransitionPieces in the selected room
	var transitions = _find_transition_pieces_in_scene(current_room_path)
	
	if transitions.is_empty():
		arrival_list.add_item("No TransitionPieces found in this room")
		arrival_list.set_item_disabled(0, true)
		status_label.text = "Warning: No arrival points available"
	else:
		for trans in transitions:
			arrival_list.add_item(trans["name"])
			arrival_list.set_item_metadata(arrival_list.item_count - 1, trans)
		status_label.text = ""
	
	_validate_selection()

func _validate_selection() -> void:
	var room_idx = room_list.get_selected_items()
	var arrival_idx = arrival_list.get_selected_items()
	
	if room_idx.is_empty():
		apply_button.disabled = true
		status_label.text = "Please select a destination room"
		return
	
	if arrival_idx.is_empty():
		apply_button.disabled = true
		status_label.text = "Please select an arrival point"
		return
	
	# Check if the selected arrival item is disabled (e.g., "No TransitionPieces found")
	if arrival_list.is_item_disabled(arrival_idx[0]):
		apply_button.disabled = true
		status_label.text = "Please select a valid arrival point"
		return
	
	var arrival_data = arrival_list.get_item_metadata(arrival_idx[0])
	print("DEBUG: arrival_data = ", arrival_data, " type = ", typeof(arrival_data))
	if arrival_data == null:
		apply_button.disabled = true
		status_label.text = "No arrival point data (null)"
		return
	
	if typeof(arrival_data) != TYPE_DICTIONARY:
		apply_button.disabled = true
		status_label.text = "Invalid arrival point data type: " + str(typeof(arrival_data))
		return
	
	apply_button.disabled = false
	status_label.text = ""

func _on_apply() -> void:
	var room_indices = room_list.get_selected_items()
	var arrival_indices = arrival_list.get_selected_items()
	
	if room_indices.is_empty() or arrival_indices.is_empty():
		return
	
	var room_data = room_list.get_item_metadata(room_indices[0])
	var arrival_data = arrival_list.get_item_metadata(arrival_indices[0])
	
	var dest_path = room_data["path"]
	var dest_uid = room_data["uid"]
	var arrival_node_name = arrival_data["name"]
	
	if transition_piece == null:
		push_error("TransitionPiece is null")
		return
	
	# Update the source TransitionPiece
	var undo_redo = EditorInterface.get_editor_undo_redo()
	undo_redo.create_action("Configure Transition Exit")
	
	# Update target and appear_at_node on source
	undo_redo.add_undo_property(transition_piece, "target", transition_piece.target)
	undo_redo.add_undo_property(transition_piece, "appear_at_node", transition_piece.appear_at_node)
	undo_redo.add_do_property(transition_piece, "target", dest_uid)
	undo_redo.add_do_property(transition_piece, "appear_at_node", arrival_node_name)
	
	# Apply changes to source
	transition_piece.target = dest_uid
	transition_piece.appear_at_node = arrival_node_name
	transition_piece.notify_property_list_changed()
	
	# Try to update bidirectionally
	var bidirectional_result = _update_bidirectional_connection(dest_path, arrival_node_name)
	
	undo_redo.commit_action()
	
	if bidirectional_result.is_empty():
		print("Transition configured successfully!")
	else:
		push_warning(bidirectional_result)
	
	hide()

func _update_bidirectional_connection(dest_scene_path: String, arrival_node_name: String) -> String:
	"""
	Update the destination scene to create/update the return transition.
	Returns empty string on success, error message on failure.
	"""
	var current_scene = EditorInterface.get_edited_scene_root()
	if current_scene == null:
		return "Could not determine current scene"
	
	var current_scene_path = current_scene.scene_file_path
	var current_room_name = transition_piece.name
	
	# Load destination packed scene
	var dest_packed = load(dest_scene_path) as PackedScene
	if dest_packed == null:
		return "Failed to load destination scene: %s" % dest_scene_path
	
	var dest_state = dest_packed.get_state()
	var found_transition_idx = -1
	var found_transition_node_name = ""
	
	# Find existing TransitionPiece that points back to current room
	for i in range(dest_state.get_node_count()):
		var node_instance = dest_state.get_node_instance(i)
		if node_instance == null:
			continue
		
		# Check if this is a TransitionPiece instance
		var instance_path = node_instance.resource_path
		if instance_path != TRANSITION_PIECE_SCENE:
			continue
		
		var node_name = dest_state.get_node_name(i)
		if node_name == current_room_name:
			found_transition_idx = i
			found_transition_node_name = node_name
			break
	
	# Reload scene for editing
	var dest_scene = dest_packed.instantiate()
	if dest_scene == null:
		return "Failed to instantiate destination scene"
	
	var return_transition: TransitionPiece = null
	
	if found_transition_idx >= 0:
		# Update existing transition
		return_transition = dest_scene.get_node_or_null(found_transition_node_name)
		if return_transition == null:
			return "Found transition index but couldn't get node: %s" % found_transition_node_name
	else:
		# Create new TransitionPiece
		return_transition = load(TRANSITION_PIECE_SCENE).instantiate()
		return_transition.name = current_room_name
		dest_scene.add_child(return_transition)
		return_transition.owner = dest_scene
	
	# Update the return transition properties
	var current_scene_uid = _get_scene_uid(current_scene_path)
	return_transition.target = current_scene_uid
	return_transition.appear_at_node = transition_piece.name
	
	# Save the modified scene
	var new_packed = PackedScene.new()
	var pack_result = new_packed.pack(dest_scene)
	if pack_result != OK:
		dest_scene.free()
		return "Failed to pack modified scene: %s" % error_string(pack_result)
	
	var save_result = ResourceSaver.save(new_packed, dest_scene_path)
	dest_scene.free()
	
	if save_result != OK:
		return "Failed to save destination scene: %s" % error_string(save_result)
	
	# Reload the scene in editor
	EditorInterface.reload_scene_from_path(dest_scene_path)
	
	return ""

func _find_room_scenes(path: String = "res://scenes/") -> PackedStringArray:
	var results: PackedStringArray = []
	var dir = DirAccess.open(path)
	
	if not dir:
		push_error("Could not open directory: " + path)
		return results
	
	dir.list_dir_begin()
	var file_name = dir.get_next()
	
	while file_name != "":
		var full_path = path.path_join(file_name)
		
		if dir.current_is_dir():
			if not file_name.begins_with(".") and file_name != "addons":
				results.append_array(_find_room_scenes(full_path))
		else:
			if file_name.ends_with(".tscn") and file_name.begins_with("kq4_"):
				results.append(full_path)
		
		file_name = dir.get_next()
	
	dir.list_dir_end()
	return results

func _get_scene_uid(scene_path: String) -> String:
	"""Get the UID for a scene file."""
	var file = FileAccess.open(scene_path, FileAccess.READ)
	if not file:
		# Fallback to using the path
		return scene_path
	
	var first_line = file.get_line()
	file.close()
	
	# Parse uid="uid://xxxxx" from the header
	var regex = RegEx.new()
	regex.compile('uid="(uid://[^"]+)"')
	var result = regex.search(first_line)
	
	if result:
		return result.get_string(1)
	
	# Fallback: use ResourceUID
	var uid_path = ResourceUID.path_to_uid(scene_path)
	if uid_path.begins_with("uid://"):
		return uid_path
	
	return scene_path

func _find_transition_pieces_in_scene(scene_path: String) -> Array[Dictionary]:
	"""Find all TransitionPiece nodes in a scene file."""
	var results: Array[Dictionary] = []
	
	var packed = load(scene_path) as PackedScene
	if packed == null:
		return results
	
	var state = packed.get_state()
	
	for i in range(state.get_node_count()):
		var node_instance = state.get_node_instance(i)
		if node_instance == null:
			continue
		
		# Check if this is a TransitionPiece instance
		var instance_path = node_instance.resource_path
		if instance_path != TRANSITION_PIECE_SCENE:
			continue
		
		var node_name = state.get_node_name(i)
		results.append({
			"name": node_name,
			"index": i
		})
	
	return results
