extends Node3D

@onready var world: Node3D = $World/ChunkManager
@onready var plankton_follower: Node3D = $PlanktonFollower

var inventory: Inventory = Inventory.new()
var _inventory_ui: CanvasLayer = null
var _pause_menu: Control = null

var _last_chunk_update_pos: Vector3 = Vector3(99999, 99999, 99999)

# Multiplayer state
var _my_dolphin: CharacterBody3D = null
var _peer_dolphins: Dictionary = {}  # peer_id -> CharacterBody3D
var _world_sync: Node = null
var _chat_manager: Node = null

# Dolphin scene cached
var _dolphin_scene: PackedScene = preload("res://scenes/Dolphin.tscn")


func _ready() -> void:
	add_to_group("main")

	# Setup WorldSyncComponent on ChunkManager
	_world_sync = load("res://scripts/net/WorldSyncComponent.gd").new()
	_world_sync.name = "WorldSyncComponent"
	world.add_child(_world_sync)

	world.world_seed = 12345

	var mode := NetworkManager.current_mode

	if NetworkManager.is_solo():
		_spawn_local_dolphin()
		_spawn_mobs()
	elif NetworkManager.is_server():
		# HOST or DEDICATED: spawn mobs, connect peer signals, spawn dolphin for existing peers
		_spawn_mobs()
		NetworkManager.peer_connected.connect(_on_net_peer_connected)
		NetworkManager.peer_disconnected.connect(_on_net_peer_disconnected)
		if mode == NetworkManager.Mode.HOST:
			# Host also plays — spawn own dolphin
			_spawn_networked_dolphin(multiplayer.get_unique_id())
		# Spawn dolphins for any peers already connected (edge case)
		for peer_id in multiplayer.get_peers():
			_spawn_networked_dolphin(peer_id)
	elif NetworkManager.is_client():
		# CLIENT: only dolphin for self, world updates come from server
		NetworkManager.peer_disconnected.connect(_on_net_peer_disconnected)
		_spawn_networked_dolphin(multiplayer.get_unique_id())

	# Chat (all modes except dedicated headless)
	if mode != NetworkManager.Mode.DEDICATED or not DisplayServer.get_name() == "headless":
		_chat_manager = load("res://scripts/net/ChatManager.gd").new()
		_chat_manager.name = "ChatManager"
		add_child(_chat_manager)

	# Audio
	AudioManager.play_ambient_loop("underwater_ambient")
	AudioManager.play_music("underwater_theme")
	AudioManager.play_whale_call_random()

	# Starting inventory
	inventory.add_item(2, 10)
	inventory.add_item(6, 5)
	inventory.add_item(8, 2)


func _spawn_local_dolphin() -> void:
	var dolphin: CharacterBody3D = _dolphin_scene.instantiate()
	dolphin.name = "Dolphin"
	dolphin.position = Vector3(0, 55, 0)
	add_child(dolphin)
	_my_dolphin = dolphin
	_connect_dolphin_signals(dolphin)

	var hud: CanvasLayer = dolphin.get_node_or_null("HUD")
	if is_instance_valid(hud) and hud.has_method("connect_to_dolphin"):
		hud.connect_to_dolphin(dolphin)

	dolphin.add_to_group("player")

	var ui_scene: PackedScene = load("res://scenes/InventoryUI.tscn")
	if ui_scene != null:
		_inventory_ui = ui_scene.instantiate() as CanvasLayer
		add_child(_inventory_ui)
		_inventory_ui.setup(inventory)
	else:
		push_error("Main: could not load InventoryUI.tscn")

	var pause_scene: PackedScene = preload("res://scenes/PauseMenu.tscn")
	_pause_menu = pause_scene.instantiate() as Control
	add_child(_pause_menu)
	_pause_menu.hide()

	plankton_follower.global_position = dolphin.position


func _spawn_networked_dolphin(peer_id: int) -> void:
	var dolphin: CharacterBody3D = _dolphin_scene.instantiate()
	dolphin.name = "Dolphin_%d" % peer_id
	dolphin.position = Vector3(0, 55, 0)
	add_child(dolphin)

	# Attach sync component
	var sync_comp: Node = load("res://scripts/net/PlayerSyncComponent.gd").new()
	sync_comp.name = "PlayerSyncComponent"
	dolphin.add_child(sync_comp)
	sync_comp.setup(peer_id)

	var my_id := multiplayer.get_unique_id()
	if peer_id == my_id:
		_my_dolphin = dolphin
		_connect_dolphin_signals(dolphin)
		dolphin.add_to_group("player")

		var hud: CanvasLayer = dolphin.get_node_or_null("HUD")
		if is_instance_valid(hud) and hud.has_method("connect_to_dolphin"):
			hud.connect_to_dolphin(dolphin)

		var ui_scene: PackedScene = load("res://scenes/InventoryUI.tscn")
		if ui_scene != null:
			_inventory_ui = ui_scene.instantiate() as CanvasLayer
			add_child(_inventory_ui)
			_inventory_ui.setup(inventory)

		var pause_scene: PackedScene = preload("res://scenes/PauseMenu.tscn")
		_pause_menu = pause_scene.instantiate() as Control
		add_child(_pause_menu)
		_pause_menu.hide()

		plankton_follower.global_position = dolphin.position
	else:
		# Remote dolphin: disable input processing
		dolphin.set_process_unhandled_input(false)
		if dolphin.has_method("set_physics_process"):
			# Keep physics but no input
			pass
		_peer_dolphins[peer_id] = dolphin


func _despawn_peer_dolphin(peer_id: int) -> void:
	if _peer_dolphins.has(peer_id):
		var d: CharacterBody3D = _peer_dolphins[peer_id]
		if is_instance_valid(d):
			d.queue_free()
		_peer_dolphins.erase(peer_id)


func _spawn_mobs() -> void:
	var MobSpawnerScene: PackedScene = load("res://scenes/mobs/MobSpawner.tscn")
	if MobSpawnerScene == null:
		return
	var mob_spawner: Node3D = MobSpawnerScene.instantiate()
	add_child(mob_spawner)
	if is_instance_valid(_my_dolphin):
		mob_spawner.player = mob_spawner.get_path_to(_my_dolphin)


func _connect_dolphin_signals(dolphin: CharacterBody3D) -> void:
	if dolphin.has_signal("block_break_requested"):
		dolphin.block_break_requested.connect(_on_block_break)
	if dolphin.has_signal("block_place_requested"):
		dolphin.block_place_requested.connect(_on_block_place)
	if dolphin.has_signal("echolocation_triggered"):
		dolphin.echolocation_triggered.connect(_on_echolocation)
	if dolphin.has_signal("hotbar_scroll"):
		dolphin.hotbar_scroll.connect(inventory.scroll_hotbar)


func _on_net_peer_connected(peer_id: int) -> void:
	if NetworkManager.is_server():
		_spawn_networked_dolphin(peer_id)


func _on_net_peer_disconnected(peer_id: int) -> void:
	_despawn_peer_dolphin(peer_id)


func _process(_delta: float) -> void:
	if not is_instance_valid(_my_dolphin):
		return
	if _my_dolphin.global_position.distance_to(_last_chunk_update_pos) > 4.0:
		world.update_player_position(_my_dolphin.global_position)
		_last_chunk_update_pos = _my_dolphin.global_position
	plankton_follower.global_position = _my_dolphin.global_position


func _unhandled_input(event: InputEvent) -> void:
	if not event.is_action_pressed("escape"):
		return
	if _inventory_ui != null and _inventory_ui.get("_is_open"):
		_inventory_ui.call("_toggle_inventory")
		get_viewport().set_input_as_handled()
		return
	if is_instance_valid(_pause_menu):
		if _pause_menu.visible:
			_pause_menu.hide_pause()
		else:
			_pause_menu.show_pause()
		get_viewport().set_input_as_handled()


func _on_block_break(hit_position: Vector3, _normal: Vector3) -> void:
	if NetworkManager.is_solo():
		var broken_id: int = world.break_block(hit_position)
		if broken_id > 0:
			inventory.add_item(broken_id, 1)
			AudioManager.play_bubble_sfx(hit_position)
	else:
		_world_sync.server_break_block(hit_position)
		AudioManager.play_bubble_sfx(hit_position)


func _on_block_place(hit_position: Vector3, normal: Vector3) -> void:
	var selected: Variant = inventory.get_selected_item()
	if selected == null:
		return
	if not ItemDatabase.is_placeable(selected["item_id"]):
		return

	var place_pos := hit_position + normal * 0.5
	var block_coord := Vector3(floor(place_pos.x), floor(place_pos.y), floor(place_pos.z))
	var block_center := block_coord + Vector3(0.5, 0.5, 0.5)

	if is_instance_valid(_my_dolphin):
		var dolphin_pos: Vector3 = _my_dolphin.global_position
		var dist := block_center - dolphin_pos
		if abs(dist.x) < 1.0 and abs(dist.y) < 1.8 and abs(dist.z) < 1.0:
			return

	if NetworkManager.is_solo():
		if world.place_block(place_pos, selected["item_id"]):
			inventory.remove_item_from_slot(inventory.selected_hotbar, 1)
	else:
		_world_sync.server_place_block(place_pos, selected["item_id"])
		inventory.remove_item_from_slot(inventory.selected_hotbar, 1)


func _on_echolocation(position: Vector3, _radius: float) -> void:
	AudioManager.play_bubble_sfx(position)


# Called by WorldSyncComponent when server confirms a block was broken by this client
func _on_remote_block_broken(broken_id: int) -> void:
	if broken_id > 0:
		inventory.add_item(broken_id, 1)
