extends Node3D

@export var player: NodePath = NodePath("")
@export var spawn_radius: float = 25.0
@export var despawn_radius: float = 40.0
@export var max_fish_schools: int = 3
@export var max_jellyfish: int = 5
@export var max_sharks: int = 1

const FISH_SCHOOL_SCENE := "res://scenes/mobs/FishSchool.tscn"
const JELLYFISH_SCENE := "res://scenes/mobs/Jellyfish.tscn"
const SHARK_SCENE := "res://scenes/mobs/Shark.tscn"

var _player_node: CharacterBody3D = null
var _chunk_manager: Node = null
var _timer: float = 0.0
const SPAWN_INTERVAL: float = 5.0

var _fish_schools: Array[Node3D] = []
var _jellyfish_list: Array[Node3D] = []
var _sharks: Array[Node3D] = []


func _ready() -> void:
	_resolve_player()
	_find_chunk_manager()


func _resolve_player() -> void:
	if not player.is_empty():
		var n: Node = get_node_or_null(player)
		if n is CharacterBody3D:
			_player_node = n as CharacterBody3D
	if _player_node == null:
		var players := get_tree().get_nodes_in_group("player")
		if players.size() > 0:
			_player_node = players[0] as CharacterBody3D


func _find_chunk_manager() -> void:
	# Try common path
	var cm: Node = get_node_or_null("/root/Main/World/ChunkManager")
	if cm != null:
		_chunk_manager = cm
		return
	# Fallback: search by class name
	var candidates := get_tree().get_nodes_in_group("chunk_manager")
	if candidates.size() > 0:
		_chunk_manager = candidates[0]


func _physics_process(delta: float) -> void:
	_timer += delta
	if _timer >= SPAWN_INTERVAL:
		_timer = 0.0
		_resolve_player()
		_tick_spawner()


func _tick_spawner() -> void:
	if not is_instance_valid(_player_node):
		return

	var player_pos: Vector3 = _player_node.global_position

	# --- Despawn far mobs ---
	_cull_array(_fish_schools, player_pos)
	_cull_array(_jellyfish_list, player_pos)
	_cull_array(_sharks, player_pos)

	# --- Spawn fish schools ---
	while _fish_schools.size() < max_fish_schools:
		var pos: Vector3 = _random_spawn_pos(player_pos)
		if _is_valid_spawn(pos):
			var inst: Node3D = _load_and_instantiate(FISH_SCHOOL_SCENE, pos)
			if inst != null:
				_fish_schools.append(inst)
			else:
				break

	# --- Spawn jellyfish ---
	while _jellyfish_list.size() < max_jellyfish:
		var pos: Vector3 = _random_spawn_pos(player_pos)
		if _is_valid_spawn(pos):
			var inst: Node3D = _load_and_instantiate(JELLYFISH_SCENE, pos)
			if inst != null:
				_connect_mob_signal(inst)
				_jellyfish_list.append(inst)
			else:
				break

	# --- Spawn sharks ---
	while _sharks.size() < max_sharks:
		var pos: Vector3 = _random_spawn_pos(player_pos)
		if _is_valid_spawn(pos):
			var inst: Node3D = _load_and_instantiate(SHARK_SCENE, pos)
			if inst != null:
				_connect_mob_signal(inst)
				_sharks.append(inst)
			else:
				break


func _cull_array(arr: Array[Node3D], player_pos: Vector3) -> void:
	var i: int = arr.size() - 1
	while i >= 0:
		if not is_instance_valid(arr[i]):
			arr.remove_at(i)
		elif arr[i].global_position.distance_to(player_pos) > despawn_radius:
			arr[i].queue_free()
			arr.remove_at(i)
		i -= 1


func _random_spawn_pos(player_pos: Vector3) -> Vector3:
	var angle: float = randf_range(0.0, TAU)
	var dist: float = randf_range(15.0, spawn_radius)
	var depth: float = randf_range(-40.0, 50.0)
	return Vector3(
		player_pos.x + cos(angle) * dist,
		depth,
		player_pos.z + sin(angle) * dist
	)


func _is_valid_spawn(pos: Vector3) -> bool:
	if _chunk_manager == null:
		return true
	if not _chunk_manager.has_method("get_block"):
		return true
	var block_id: int = _chunk_manager.call("get_block", pos)
	# Block IDs 2..8 are solid — don't spawn inside solid blocks
	if block_id >= 2 and block_id <= 8:
		return false
	return true


func _load_and_instantiate(scene_path: String, pos: Vector3) -> Node3D:
	var packed: PackedScene = load(scene_path)
	if packed == null:
		return null
	var inst: Node3D = packed.instantiate() as Node3D
	if inst == null:
		return null
	get_tree().current_scene.add_child(inst)
	inst.global_position = pos
	return inst


func _connect_mob_signal(mob: Node3D) -> void:
	if mob.has_signal("attacked_player"):
		if not mob.is_connected("attacked_player", _on_mob_attacked_player):
			mob.attacked_player.connect(_on_mob_attacked_player)


func _on_mob_attacked_player(dmg: float) -> void:
	if is_instance_valid(_player_node):
		if _player_node.has_method("take_damage"):
			_player_node.take_damage(dmg)
		elif "health" in _player_node:
			_player_node.health -= dmg
