extends CharacterBody3D

signal attacked_player(damage: float)

@export var max_health: float = 50.0
@export var damage: float = 15.0
@export var detection_radius: float = 12.0
@export var move_speed: float = 4.0
@export var chase_speed: float = 7.0

enum State { PATROL, CHASE, ATTACK }

var health: float
var _state: State = State.PATROL
var _time: float = 0.0
var _patrol_points: Array[Vector3] = []
var _patrol_index: int = 0
var _attack_cooldown: float = 0.0
var _player: CharacterBody3D = null

# Visual
var _body: MeshInstance3D
var _head: MeshInstance3D
var _tail: MeshInstance3D
var _fin: MeshInstance3D


func _ready() -> void:
	health = max_health
	_build_visuals()
	_find_player()
	_generate_patrol_points()


func _find_player() -> void:
	var players := get_tree().get_nodes_in_group("player")
	if players.size() > 0:
		_player = players[0] as CharacterBody3D


func _build_visuals() -> void:
	# Top dark grey material
	var mat_dark := StandardMaterial3D.new()
	mat_dark.albedo_color = Color(0.165, 0.208, 0.251)  # #2a3540
	mat_dark.roughness = 0.8

	# Bottom dirty white material
	var mat_white := StandardMaterial3D.new()
	mat_white.albedo_color = Color(0.816, 0.835, 0.847)  # #d0d5d8
	mat_white.roughness = 0.8

	# Body (central capsule)
	_body = MeshInstance3D.new()
	var body_mesh := CapsuleMesh.new()
	body_mesh.radius = 0.4
	body_mesh.height = 1.6
	_body.mesh = body_mesh
	_body.material_override = mat_dark
	_body.rotation.z = PI * 0.5
	add_child(_body)

	# Head (smaller capsule in front)
	_head = MeshInstance3D.new()
	var head_mesh := CapsuleMesh.new()
	head_mesh.radius = 0.28
	head_mesh.height = 0.8
	_head.mesh = head_mesh
	_head.material_override = mat_dark
	_head.rotation.z = PI * 0.5
	_head.position = Vector3(0.95, 0.0, 0.0)
	add_child(_head)

	# Tail (smaller capsule behind)
	_tail = MeshInstance3D.new()
	var tail_mesh := CapsuleMesh.new()
	tail_mesh.radius = 0.2
	tail_mesh.height = 0.7
	_tail.mesh = tail_mesh
	_tail.material_override = mat_dark
	_tail.rotation.z = PI * 0.5
	_tail.position = Vector3(-0.95, 0.0, 0.0)
	add_child(_tail)

	# Dorsal fin (prism)
	_fin = MeshInstance3D.new()
	var prism := PrismMesh.new()
	prism.size = Vector3(0.5, 0.5, 0.15)
	_fin.mesh = prism
	_fin.material_override = mat_dark
	_fin.position = Vector3(0.1, 0.4, 0.0)
	add_child(_fin)

	# Bottom lighter area (belly)
	var belly := MeshInstance3D.new()
	var belly_mesh := CapsuleMesh.new()
	belly_mesh.radius = 0.38
	belly_mesh.height = 1.4
	belly.mesh = belly_mesh
	belly.material_override = mat_white
	belly.rotation.z = PI * 0.5
	belly.position = Vector3(0.0, -0.05, 0.0)
	belly.scale = Vector3(1.0, 0.3, 1.0)
	add_child(belly)

	# Collision
	var col := CollisionShape3D.new()
	var cshape := CapsuleShape3D.new()
	cshape.radius = 0.4
	cshape.height = 2.4
	col.shape = cshape
	col.rotation.z = PI * 0.5
	add_child(col)


func _generate_patrol_points() -> void:
	_patrol_points.clear()
	for _i: int in range(3):
		_patrol_points.append(global_position + Vector3(
			randf_range(-15.0, 15.0),
			randf_range(-3.0, 3.0),
			randf_range(-15.0, 15.0)
		))


func _physics_process(delta: float) -> void:
	_time += delta

	if _player == null:
		_find_player()

	_attack_cooldown = max(0.0, _attack_cooldown - delta)

	var player_dist: float = INF
	if is_instance_valid(_player):
		player_dist = global_position.distance_to(_player.global_position)

	match _state:
		State.PATROL:
			_do_patrol(delta)
			if player_dist < detection_radius:
				_state = State.CHASE

		State.CHASE:
			if is_instance_valid(_player):
				var dir: Vector3 = (_player.global_position - global_position).normalized()
				velocity = dir * chase_speed
				if player_dist < 2.0:
					_state = State.ATTACK
			if player_dist > detection_radius * 2.0:
				_state = State.PATROL
				_generate_patrol_points()
				_patrol_index = 0

		State.ATTACK:
			if is_instance_valid(_player) and _attack_cooldown <= 0.0:
				attacked_player.emit(damage)
				_attack_cooldown = 2.0
			if player_dist > 3.0:
				_state = State.CHASE

	# Smooth rotation toward velocity
	if velocity.length_squared() > 0.1:
		var target_basis: Basis = Basis.looking_at(velocity.normalized(), Vector3.UP)
		transform.basis = transform.basis.slerp(target_basis, delta * 4.0)

	# Tail wag animation
	if is_instance_valid(_tail):
		_tail.rotation.y = sin(_time * 5.0) * 0.4

	move_and_slide()


func _do_patrol(delta: float) -> void:
	if _patrol_points.is_empty():
		_generate_patrol_points()
		return

	var target: Vector3 = _patrol_points[_patrol_index]
	var dir: Vector3 = target - global_position
	if dir.length() < 1.0:
		_patrol_index = (_patrol_index + 1) % _patrol_points.size()
	else:
		velocity = dir.normalized() * move_speed


func take_damage(dmg: float) -> void:
	health -= dmg
	if health <= 0.0:
		queue_free()
