extends "res://scripts/enemy_base.gd"

# Enemy Hand: hidden until player near (EmergeArea), then emerge -> idle.
# Moves toward player if in PlayerInterestArea, else random. Collides with walls.
# If player enters GrabPlayerArea and alive: grab, lock player, snatch anim, deal damage, release + knockback.

enum HandState {HIDDEN, EMERGING, IDLE, GRABBING}

var state: HandState = HandState.HIDDEN
var players_in_interest: Array[Node] = []
var grabbed_player: Node = null
var random_move_dir: Vector2 = Vector2.ZERO
var random_move_timer: float = 0.0
var grab_cooldown_timer: float = 0.0
const RANDOM_MOVE_INTERVAL: float = 1.2
const SNATCH_DURATION: float = 0.4
const SNATCH_DAMAGE: float = 12.0
const GRAB_COOLDOWN: float = 2.0 # Cooldown after grabbing before can grab again
const TILE_SIZE: int = 16
const TILE_STRIDE: int = 17 # 16 + separation 1
@onready var emerge_area: Area2D = $EmergeArea
@onready var grab_area: Area2D = $GrabPlayerArea
@onready var interest_area: Area2D = $PlayerInterestArea
@onready var anim_player: AnimationPlayer = $AnimationPlayer
@onready var hand_sprite: Sprite2D = $Sprite2D
var _tile_particle_scene: PackedScene = null
var blood_scene: PackedScene = preload("res://scenes/blood_clot.tscn")


func _ready() -> void:
	super._ready()
	max_health = 25.0
	current_health = max_health
	move_speed = 16.8 # 60% of 28.0 - slower chase/random movement
	damage = SNATCH_DAMAGE
	exp_reward = 8.0
	collision_layer = 2
	collision_mask = 1 | 2 | 64

	# Start hidden
	modulate.a = 0.0
	# Areas detect players (layer 1)
	for area in [emerge_area, grab_area, interest_area]:
		if area:
			area.set_collision_mask_value(1, true)
			area.monitoring = true
			area.monitorable = false

	if anim_player:
		if not anim_player.animation_finished.is_connected(_on_animation_finished):
			anim_player.animation_finished.connect(_on_animation_finished)


func _initialize_character_stats() -> void:
	super._initialize_character_stats()
	character_stats.baseStats.str = 10
	character_stats.baseStats.end = 6
	character_stats.baseStats.dex = 8
	character_stats.hp = character_stats.maxhp
	character_stats.mp = character_stats.maxmp
	max_health = character_stats.maxhp
	current_health = character_stats.hp


func _check_player_collision() -> void:
	# No contact-based attack; we use GrabPlayerArea instead
	pass


func _die() -> void:
	if grabbed_player != null and is_instance_valid(grabbed_player):
		var v = grabbed_player
		grabbed_player = null
		if v.has_method("rpc_released_from_enemy_hand"):
			var pid = v.get_multiplayer_authority()
			if multiplayer.get_unique_id() == pid:
				v.rpc_released_from_enemy_hand()
			elif pid != 0:
				v.rpc_released_from_enemy_hand.rpc_id(pid)
			else:
				v.rpc_released_from_enemy_hand.rpc()
	
	# Spawn blood clots (similar to player/enemy death)
	_spawn_blood_clots()
	
	# Split hand into 4 pieces that fly in all directions
	_spawn_hand_pieces()
	
	super._die()

func _spawn_blood_clots():
	# Spawn blood clots when enemy hand dies (similar to player/enemy death)
	if not is_multiplayer_authority():
		return
	
	if not blood_scene:
		return
	
	var parent = get_parent()
	if not parent:
		var game_world = get_tree().get_first_node_in_group("game_world")
		if game_world:
			parent = game_world.get_node_or_null("Entities")
		if not parent:
			return
	
	# Spawn 12 blood clots (same as player/enemy)
	for i in 12:
		var angle = randf_range(0, TAU)
		var speed = randf_range(50, 100)
		var initial_velocityZ = randf_range(50, 90)
		var b = blood_scene.instantiate() as CharacterBody2D
		b.scale = Vector2(randf_range(0.3, 2), randf_range(0.3, 2))
		b.global_position = global_position
		
		# Set initial velocities
		var direction = Vector2.from_angle(angle)
		b.velocity = direction * speed
		b.velocityZ = initial_velocityZ
		parent.call_deferred("add_child", b)

func _spawn_hand_pieces():
	# Split enemy hand into 4 pieces that fly in all directions (like tile particles)
	if not is_multiplayer_authority():
		return
	
	if not hand_sprite or not hand_sprite.texture:
		return
	
	if not _tile_particle_scene:
		_tile_particle_scene = load("res://scenes/explosion_tile_particle.tscn") as PackedScene
	if not _tile_particle_scene:
		return
	
	var parent = get_parent()
	if not parent:
		var game_world = get_tree().get_first_node_in_group("game_world")
		if game_world:
			parent = game_world.get_node_or_null("Entities")
		if not parent:
			return
	
	# Get current sprite frame
	var current_frame = hand_sprite.frame if hand_sprite else 0
	var hframes = hand_sprite.hframes if hand_sprite else 4
	var vframes = hand_sprite.vframes if hand_sprite else 4
	
	# Calculate frame position in texture
	var frame_x = current_frame % hframes
	var frame_y = int(float(current_frame) / float(hframes))
	
	# Get texture size and calculate frame size
	var tex = hand_sprite.texture
	var frame_width = float(tex.get_width()) / float(hframes)
	var frame_height = float(tex.get_height()) / float(vframes)
	
	# Calculate the 4 quadrants of the current frame
	var half_width = frame_width / 2.0
	var half_height = frame_height / 2.0
	
	var base_x = frame_x * frame_width
	var base_y = frame_y * frame_height
	
	# 4 quadrants: top-left, top-right, bottom-left, bottom-right
	var regions = [
		Rect2(base_x, base_y, half_width, half_height), # Top-left
		Rect2(base_x + half_width, base_y, half_width, half_height), # Top-right
		Rect2(base_x, base_y + half_height, half_width, half_height), # Bottom-left
		Rect2(base_x + half_width, base_y + half_height, half_width, half_height) # Bottom-right
	]
	
	# 4 directions: up-left, up-right, down-left, down-right
	var directions = [
		Vector2(-1, -1).normalized(), # Up-left
		Vector2(1, -1).normalized(), # Up-right
		Vector2(-1, 1).normalized(), # Down-left
		Vector2(1, 1).normalized() # Down-right
	]
	
	# Spawn 4 pieces
	for i in range(4):
		var p = _tile_particle_scene.instantiate() as CharacterBody2D
		var piece_sprite = p.get_node_or_null("Sprite2D") as Sprite2D
		if not piece_sprite:
			p.queue_free()
			continue
		
		# Use the hand texture and region
		piece_sprite.texture = tex
		piece_sprite.region_enabled = true
		piece_sprite.region_rect = regions[i]
		
		# Position at hand location
		p.global_position = global_position
		
		# Fly in the direction for this piece
		var direction = directions[i]
		var speed = randf_range(200.0, 300.0) # Fast enough to see them fly
		p.velocity = direction * speed
		p.angular_velocity = randf_range(-10.0, 10.0)
		p.position_z = 0.0
		p.velocity_z = randf_range(80.0, 140.0) # Upward burst, then gravity
		
		# Use call_deferred to avoid physics query flush errors
		parent.call_deferred("add_child", p)


func _ai_behavior(delta: float) -> void:
	# Update grab cooldown timer
	if grab_cooldown_timer > 0.0:
		grab_cooldown_timer -= delta
	
	if state == HandState.HIDDEN or state == HandState.EMERGING:
		velocity = Vector2.ZERO
		return
	if state == HandState.GRABBING:
		velocity = Vector2.ZERO
		# Update grabbed player position to follow hand (slightly above)
		if grabbed_player and is_instance_valid(grabbed_player):
			var target_pos = global_position + Vector2(0, -12) # Slightly above the hand
			# Smoothly move player to hand position (only on authority)
			if is_multiplayer_authority():
				grabbed_player.global_position = grabbed_player.global_position.lerp(target_pos, delta * 8.0)
		return

	# IDLE: move toward player if in interest, else random
	var target: Node = null
	for p in players_in_interest:
		if is_instance_valid(p) and p.is_in_group("player") and (not "is_dead" in p or not p.is_dead):
			target = p
			break

	if target:
		var dir = (target.global_position - global_position).normalized()
		velocity = dir * move_speed
	else:
		# Random movement
		random_move_timer -= delta
		if random_move_timer <= 0.0:
			random_move_timer = RANDOM_MOVE_INTERVAL
			var ang = randf() * TAU
			random_move_dir = Vector2(cos(ang), sin(ang))
		velocity = random_move_dir * move_speed

	move_and_slide()


func _on_emerge_area_body_entered(body: Node2D) -> void:
	if state != HandState.HIDDEN:
		return
	if not body.is_in_group("player"):
		return
	if "is_dead" in body and body.is_dead:
		return
	if not is_multiplayer_authority():
		return

	state = HandState.EMERGING
	modulate.a = 1.0
	if anim_player and anim_player.has_animation("emerge"):
		anim_player.play("emerge")
	
	# Spawn tile particles when emerging
	_spawn_emerge_tile_particles()
	
	# Sync visibility to all clients
	if multiplayer.has_multiplayer_peer() and is_inside_tree():
		_sync_hand_emerged.rpc()


func _on_animation_finished(anim_name: StringName) -> void:
	if anim_name == "emerge":
		state = HandState.IDLE
		if anim_player and anim_player.has_animation("idle"):
			anim_player.play("idle")
	# snatch completion driven by timer (SNATCH_DURATION), not animation_finished


func _on_player_interest_area_body_entered(body: Node2D) -> void:
	if not body.is_in_group("player"):
		return
	if body in players_in_interest:
		return
	players_in_interest.append(body)


func _on_player_interest_area_body_exited(body: Node2D) -> void:
	players_in_interest.erase(body)


func _on_grab_player_area_body_entered(body: Node2D) -> void:
	if state != HandState.IDLE or is_dead:
		return
	if not body.is_in_group("player"):
		return
	if "is_dead" in body and body.is_dead:
		return
	if "grabbed_by_enemy_hand" in body and body.grabbed_by_enemy_hand != null:
		return
	if grabbed_player != null:
		return
	if grab_cooldown_timer > 0.0:
		return # Still on cooldown from previous grab
	if not is_multiplayer_authority():
		return

	grabbed_player = body
	state = HandState.GRABBING
	velocity = Vector2.ZERO

	# Lock player movement via RPC (victim's authority runs their physics)
	if body.has_method("rpc_grabbed_by_enemy_hand"):
		var pid = body.get_multiplayer_authority()
		if multiplayer.get_unique_id() == pid:
			body.rpc_grabbed_by_enemy_hand(name)
		elif pid != 0:
			body.rpc_grabbed_by_enemy_hand.rpc_id(pid, name)
		else:
			body.rpc_grabbed_by_enemy_hand.rpc(name)

	if anim_player and anim_player.has_animation("snatch"):
		anim_player.play("snatch")
	get_tree().create_timer(SNATCH_DURATION).timeout.connect(_finish_snatch)


@rpc("authority", "reliable")
func _sync_hand_emerged():
	# Sync hand emergence visibility to clients
	if is_multiplayer_authority():
		return # Authority already handled it locally
	
	if state == HandState.HIDDEN:
		state = HandState.EMERGING
		modulate.a = 1.0
		if anim_player and anim_player.has_animation("emerge"):
			anim_player.play("emerge")

func _finish_snatch() -> void:
	if not is_instance_valid(self):
		return
	var victim = grabbed_player
	grabbed_player = null
	state = HandState.IDLE
	grab_cooldown_timer = GRAB_COOLDOWN # Start cooldown after grab
	if anim_player and anim_player.has_animation("idle"):
		anim_player.play("idle")
	if not is_instance_valid(victim):
		return

	# Deal damage (applies knockback via take_damage)
	var attack_pos = global_position
	if victim.has_method("rpc_take_damage"):
		var pid = victim.get_multiplayer_authority()
		if pid != 0:
			if multiplayer.get_unique_id() == pid:
				victim.take_damage(SNATCH_DAMAGE, attack_pos, false, false)
			else:
				victim.rpc_take_damage.rpc_id(pid, SNATCH_DAMAGE, attack_pos, false, false)
		else:
			victim.rpc_take_damage.rpc(SNATCH_DAMAGE, attack_pos, false, false)

	# Release player (RPC so victim's authority clears grabbed_by_enemy_hand)
	if victim.has_method("rpc_released_from_enemy_hand"):
		var pid = victim.get_multiplayer_authority()
		if multiplayer.get_unique_id() == pid:
			victim.rpc_released_from_enemy_hand()
		elif pid != 0:
			victim.rpc_released_from_enemy_hand.rpc_id(pid)
		else:
			victim.rpc_released_from_enemy_hand.rpc()


func _spawn_emerge_tile_particles():
	# Spawn tile particles from the tile the hand is emerging from (similar to bomb explosion)
	var game_world = get_tree().get_first_node_in_group("game_world")
	if not game_world:
		return
	
	var layer = game_world.get_node_or_null("Environment/DungeonLayer0")
	if not layer or not layer is TileMapLayer:
		return
	
	if not _tile_particle_scene:
		_tile_particle_scene = load("res://scenes/explosion_tile_particle.tscn") as PackedScene
	if not _tile_particle_scene:
		return
	
	var tex = load("res://assets/gfx/RPG DUNGEON VOL 3.png") as Texture2D
	if not tex:
		return
	
	var center = global_position
	var layer_pos = center - layer.global_position
	var center_cell = layer.local_to_map(layer_pos)
	
	var parent = get_parent()
	if not parent:
		parent = game_world.get_node_or_null("Entities")
	if not parent:
		return
	
	# Get the tile at the hand's position
	var cell = center_cell
	if layer.get_cell_source_id(cell) < 0:
		return # No tile at this position
	
	var atlas = layer.get_cell_atlas_coords(cell)
	var world = layer.map_to_local(cell) + layer.global_position
	
	var bx = atlas.x * TILE_STRIDE
	var by = atlas.y * TILE_STRIDE
	var h = 8.0 # TILE_SIZE / 2
	var regions = [
		Rect2(bx, by, h, h),
		Rect2(bx + h, by, h, h),
		Rect2(bx, by + h, h, h),
		Rect2(bx + h, by + h, h, h)
	]
	
	# Spawn 2-3 particles from the tile, flying outward in random directions
	var num_particles = randi_range(2, 3)
	for i in range(num_particles):
		var p = _tile_particle_scene.instantiate() as CharacterBody2D
		var spr = p.get_node_or_null("Sprite2D") as Sprite2D
		if not spr:
			p.queue_free()
			continue
		spr.texture = tex
		spr.region_enabled = true
		# Randomly pick one of the 4 tile quadrants
		var region_idx = randi() % 4
		spr.region_rect = regions[region_idx]
		p.global_position = world
		# Particles fly outward in random directions (less intense than bomb)
		var angle = randf() * TAU
		var d = Vector2(cos(angle), sin(angle))
		var speed = randf_range(150.0, 250.0) # Slower than bomb explosion
		p.velocity = d * speed
		p.angular_velocity = randf_range(-8.0, 8.0)
		p.position_z = 0.0
		p.velocity_z = randf_range(60.0, 120.0) # Upward burst, then gravity
		# Use call_deferred to avoid physics query flush errors
		parent.call_deferred("add_child", p)

func _sync_position(pos: Vector2, vel: Vector2, z_pos: float = 0.0, dir: int = 0, frame: int = 0, anim: String = "", frame_num: int = 0, state_value: int = -1) -> void:
	super._sync_position(pos, vel, z_pos, dir, frame, anim, frame_num, state_value)
	# Client: keep AnimationPlayer in sync with state if we add state sync later
	pass
