extends BaseEnemy
class_name BasicEnemy

## A basic melee enemy that chases and attacks players
## Server-authoritative AI with multiplayer support

## Movement
@export var move_speed: float = 3.0
@export var chase_range: float = 15.0
@export var attack_range: float = 2.5

## Combat
@export var attack_damage: float = 15.0
@export var attack_knockback: float = 10.0
@export var attack_cooldown: float = 1.5
@export var health_orb_drop_chance: float = 0.4  ## Chance (0-1) to drop a health orb on death
@export_category("Attack Timing")
@export var attack_startup: float = 0.3   # Wind-up before hit
@export var attack_active: float = 0.4    # Hit window duration

## References
var _hitbox: HitBox = null
var _hurtbox: HurtBox = null
var _mesh: MeshInstance3D = null

## AI State
var _attack_timer: float = 0.0
var _is_attacking: bool = false
var _original_material: Material = null
var _hit_flash_timer: float = 0.0
const HIT_FLASH_DURATION: float = 0.2

func _enter_tree():
	# Enemies are always server-authoritative
	set_multiplayer_authority(1)

func _ready():
	super._ready()

	# Wave lobsters die for good — no reviving (BaseEnemy defaults this to true)
	can_respawn = false

	# Find mesh, hitbox, and hurtbox
	_mesh = get_node_or_null("Mesh")
	_hitbox = get_node_or_null("HitBox")
	_hurtbox = get_node_or_null("HurtBox")

	# Store original material for hit flash
	if _mesh:
		_original_material = _mesh.get_surface_override_material(0)
		if not _original_material and _mesh.mesh:
			_original_material = _mesh.mesh.surface_get_material(0)

	# Setup hitbox
	if _hitbox:
		_hitbox.owner_entity = self
		_hitbox.set_stats(attack_damage, attack_knockback)
		_hitbox.hit_landed.connect(_on_hitbox_hit)

	# Setup hurtbox
	if _hurtbox:
		_hurtbox.owner_entity = self

func _process(delta):
	# Countdown timers
	if _attack_timer > 0:
		_attack_timer -= delta

	# Handle hit flash
	if _hit_flash_timer > 0:
		_hit_flash_timer -= delta
		if _hit_flash_timer <= 0:
			_reset_material()

func _physics_process(delta):
	super._physics_process(delta)

	# Only server runs AI
	if not multiplayer.is_server():
		return

	if is_dead:
		return

	# Apply gravity
	if not is_on_floor():
		velocity.y -= ProjectSettings.get_setting("physics/3d/default_gravity") * delta

	# AI behavior
	if current_target and is_instance_valid(current_target):
		_ai_combat(delta)
	else:
		# Stop moving if no target
		velocity.x = 0
		velocity.z = 0

	move_and_slide()

## Override to find nearest player without range limit
func get_nearest_player() -> Node:
	# Find all players in a very large range (essentially unlimited)
	var players = get_players_in_range(1000.0)  # 1000m range - basically unlimited

	if players.is_empty():
		return null

	var nearest_player = null
	var nearest_distance = INF

	for player in players:
		var distance = global_position.distance_to(player.global_position)
		if distance < nearest_distance:
			nearest_distance = distance
			nearest_player = player

	return nearest_player

## Update target - called by BaseEnemy
func _update_target():
	if not is_aggressive:
		return

	# Always find and target the nearest player (no range limit)
	var nearest = get_nearest_player()

	if nearest:
		# Update target if it changed
		if current_target != nearest:
			current_target = nearest
			target_changed.emit(nearest)
	else:
		# No players exist
		if current_target != null:
			current_target = null
			target_changed.emit(null)

## Combat AI behavior
func _ai_combat(delta):
	if not current_target or not is_instance_valid(current_target):
		return

	var target_pos = current_target.global_position
	var direction = (target_pos - global_position).normalized()
	var distance = global_position.distance_to(target_pos)

	# Face the target
	if direction.length() > 0.01:
		var look_dir = Vector3(direction.x, 0, direction.z)
		if look_dir.length() > 0.01:
			look_at(global_position + look_dir, Vector3.UP)

	# If in attack range, attack
	if distance <= attack_range:
		# Stop moving when attacking
		velocity.x = 0
		velocity.z = 0

		# Try to attack
		if _attack_timer <= 0 and not _is_attacking:
			_perform_attack()
	else:
		# Always chase the target (no range limit)
		velocity.x = direction.x * move_speed
		velocity.z = direction.z * move_speed

## Perform melee attack
func _perform_attack():
	if _is_attacking or not multiplayer.is_server():
		return

	# Calculate total attack duration
	var total_duration = attack_startup + attack_active
	var cooldown = max(attack_cooldown, total_duration)

	_attack_timer = cooldown
	_is_attacking = true

	# Play attack animation on all clients
	rpc("_sync_attack_animation")

	# Activate hitbox
	_activate_hitbox()

## Activate hitbox for attack
func _activate_hitbox():
	if not _hitbox or not multiplayer.is_server():
		_is_attacking = false
		return

	# STARTUP PHASE - Wait before activating
	if attack_startup > 0:
		await get_tree().create_timer(attack_startup).timeout

	if not _hitbox or not is_instance_valid(_hitbox) or is_dead:
		_is_attacking = false
		return

	# ACTIVE PHASE - Hitbox on
	_hitbox.activate()

	await get_tree().create_timer(attack_active).timeout

	# RECOVERY PHASE - Hitbox off
	if _hitbox and is_instance_valid(_hitbox):
		_hitbox.deactivate()

	_is_attacking = false

## Called when hitbox hits something
func _on_hitbox_hit(target: Node, damage_amount: float, knockback_amount: float, attacker_pos: Vector3):
	if not target or not multiplayer.is_server():
		return

	# Flash target's hurtbox
	if target is Node:
		var hurtbox = target.find_child("HurtBox", true, false)
		if hurtbox and hurtbox.has_method("flash_hit"):
			hurtbox.flash_hit()

	# Server applies damage directly
	if target is BaseUnit:
		target.take_damage(damage_amount, 1, knockback_amount, global_position)

## Play attack animation (synced to all clients)
@rpc("any_peer", "call_local", "reliable")
func _sync_attack_animation():
	# Visual feedback for attack
	# Could play animation here if you have an AnimationPlayer
	pass

## Override hurt animation to flash red
@rpc("any_peer", "call_local", "reliable")
func _play_hurt_animation():
	if _mesh:
		_flash_red()

## Flash red when hit
func _flash_red():
	if not _mesh:
		return

	_hit_flash_timer = HIT_FLASH_DURATION

	# Create red material
	var red_material = StandardMaterial3D.new()
	red_material.albedo_color = Color(1.5, 0.3, 0.3)

	# Copy properties from original if exists
	if _original_material and _original_material is StandardMaterial3D:
		var orig = _original_material as StandardMaterial3D
		red_material.metallic = orig.metallic
		red_material.roughness = orig.roughness
		red_material.albedo_texture = orig.albedo_texture

	_mesh.set_surface_override_material(0, red_material)

## Reset material
func _reset_material():
	if _mesh and _original_material:
		_mesh.set_surface_override_material(0, _original_material.duplicate())

## Handle death: drop loot, then despawn (server-authoritative, fires once per death)
func _die(killer_id: int):
	var already_dead = is_dead
	super._die(killer_id)

	if not multiplayer.is_server() or already_dead:
		return

	# Chance to drop a health orb
	if randf() < health_orb_drop_chance:
		var level = get_tree().get_current_scene()
		if level and level.has_method("spawn_health_orb"):
			level.spawn_health_orb(global_position + Vector3.UP)

	# Non-reviving enemies remove their corpse on every client after a short beat
	if not can_respawn:
		_schedule_despawn()

## Free this enemy on all peers a couple seconds after death
func _schedule_despawn():
	await get_tree().create_timer(2.0).timeout
	if is_instance_valid(self):
		rpc("_despawn")

@rpc("any_peer", "call_local", "reliable")
func _despawn():
	queue_free()

## Death callback
func _on_enemy_died(killer_id: int):
	super._on_enemy_died(killer_id)

	# Hide when dead
	if _mesh:
		_mesh.visible = false

	# Disable collision so players can walk through
	var collision_shape = get_node_or_null("CollisionShape3D")
	if collision_shape:
		collision_shape.disabled = true

	# Disable hurtbox
	var hurtbox = get_node_or_null("HurtBox")
	if hurtbox:
		hurtbox.monitorable = false

	# Deactivate hitbox
	if _hitbox:
		_hitbox.deactivate()

	print("[BasicEnemy ", name, "] killed by ", killer_id)

## Respawn callback
func _on_enemy_respawned():
	super._on_enemy_respawned()

	# Show mesh
	if _mesh:
		_mesh.visible = true
		_reset_material()

	# Re-enable collision
	var collision_shape = get_node_or_null("CollisionShape3D")
	if collision_shape:
		collision_shape.disabled = false

	# Re-enable hurtbox
	var hurtbox = get_node_or_null("HurtBox")
	if hurtbox:
		hurtbox.monitorable = true

	# Reset state
	_attack_timer = 0.0
	_is_attacking = false

	print("[BasicEnemy ", name, "] respawned")
