extends Node2D

@onready var sprite = $Sprite2D
@onready var animation_player = $AnimationPlayer
@onready var activation_area = $ActivationArea
@onready var disarm_area = $DisarmArea
@onready var detection_area = $DetectionArea

# Trap state
var is_detected: bool = false  # Becomes true when any player detects it
var is_disarmed: bool = false  # True if trap has been disarmed
var is_active: bool = false  # True when trap is currently triggering
var has_cooldown: bool = false  # Some traps can reset
var cooldown_time: float = 5.0  # Time until trap can re-activate
var cooldown_timer: float = 0.0

# Trap properties
var trap_damage: float = 15.0
var trap_type: String = "Floor_Lance"

# Per-player detection tracking (Dictionary: peer_id -> has_attempted_detection)
var player_detection_attempts: Dictionary = {}

# Disarm tracking
var disarming_player: Node = null
var disarm_progress: float = 0.0
var disarm_duration: float = 1.0
var disarm_label: Label = null

func _ready() -> void:
	# Add to trap group for detection by players
	add_to_group("trap")
	
	# Randomize trap visual based on dungeon seed
	var highbox_seed = 0
	var world = get_tree().get_first_node_in_group("game_world")
	if world and "dungeon_seed" in world:
		highbox_seed = world.dungeon_seed
	highbox_seed += int(global_position.x) * 1000 + int(global_position.y)
	
	var rng = RandomNumberGenerator.new()
	rng.seed = highbox_seed
	var index = rng.randi() % 2
	if index == 0:
		sprite.texture = load("res://assets/gfx/traps/Floor_Lance.png")
		trap_type = "Floor_Lance"
		has_cooldown = true  # Lance traps can reset
	
	# Start hidden (invisible until detected)
	sprite.modulate.a = 0.0
	
	# Setup detection area to check for players
	if detection_area:
		detection_area.body_entered.connect(_on_detection_area_body_entered)
		detection_area.body_exited.connect(_on_detection_area_body_exited)
	
	# Setup disarm area to show "DISARM" text for dwarves
	if disarm_area:
		disarm_area.body_entered.connect(_on_disarm_area_body_entered)
		disarm_area.body_exited.connect(_on_disarm_area_body_exited)

func _process(delta: float) -> void:
	# Handle cooldown timer for resetting traps
	if has_cooldown and not is_active and cooldown_timer > 0:
		cooldown_timer -= delta
		if cooldown_timer <= 0:
			# Trap has cooled down - ready to trigger again
			pass
	
	# Handle disarm progress
	if disarming_player and is_instance_valid(disarming_player):
		# Check if player is still holding grab button
		if disarming_player.is_multiplayer_authority() and Input.is_action_pressed("grab"):
			# Play disarming sound (only if not already playing)
			if $SfxDisarming.playing == false:
				$SfxDisarming.play()
			
			disarm_progress += delta
			_update_disarm_ui()
			
			if disarm_progress >= disarm_duration:
				# Disarm complete!
				_complete_disarm()
		else:
			# Player released grab - cancel disarm
			_cancel_disarm()

func _on_detection_area_body_entered(body: Node) -> void:
	# When a player enters detection range, roll perception check (once per player per game)
	if not body.is_in_group("player"):
		return
	
	if is_detected or is_disarmed:
		return  # Already detected or disarmed
	
	# Get player peer ID
	var peer_id = body.get_multiplayer_authority()
	
	# Check if this player has already attempted detection
	if player_detection_attempts.has(peer_id):
		return  # Already tried once this game
	
	# Mark that this player has attempted detection
	player_detection_attempts[peer_id] = true
	
	# Roll perception check (only on server/authority)
	if multiplayer.is_server() or not multiplayer.has_multiplayer_peer():
		_roll_perception_check(body)

func _on_detection_area_body_exited(_body: Node) -> void:
	pass  # Detection is permanent once attempted

func _roll_perception_check(player: Node) -> void:
	# Roll perception check for player
	if not player or not player.character_stats:
		return
	
	var per_stat = player.character_stats.baseStats.per + player.character_stats.get_pass("per")
	
	# Perception roll: d20 + PER modifier
	# Target DC: 15 (medium difficulty)
	var roll = randi() % 20 + 1  # 1d20
	var total = roll + int(per_stat / 2) - 5  # PER modifier: (PER - 10) / 2
	var dc = 15
	
	print(player.name, " rolls Perception: ", roll, " + ", int(per_stat / 2) - 5, " = ", total, " vs DC ", dc)
	
	if total >= dc:
		# Success! Player detects the trap
		_detect_trap(player)
	else:
		# Failure - trap remains hidden to this player
		print(player.name, " failed to detect trap")

func _detect_trap(detecting_player: Node) -> void:
	# Trap is detected - make it visible to ALL players
	is_detected = true
	
	# Make trap visible
	sprite.modulate.a = 1.0
	
	# Notify the detecting player to show alert and play sound
	if detecting_player and is_instance_valid(detecting_player) and detecting_player.has_method("_on_trap_detected"):
		detecting_player._on_trap_detected()
	
	# Sync detection to all clients (including server with call_local)
	# CRITICAL: Validate trap is still valid before sending RPC
	# Use GameWorld RPC to avoid node path issues
	if multiplayer.has_multiplayer_peer() and is_inside_tree() and is_instance_valid(self):
		if multiplayer.is_server():
			# Use GameWorld RPC with trap name instead of path
			var game_world = get_tree().get_first_node_in_group("game_world")
			if game_world and game_world.has_method("_sync_trap_state_by_name"):
				game_world._sync_trap_state_by_name.rpc(name, true, false)  # detected=true, disarmed=false
	
	print(detecting_player.name, " detected trap at ", global_position)

@rpc("authority", "call_local", "reliable")
func _sync_trap_detected() -> void:
	# Client receives trap detection notification
	# CRITICAL: Validate trap is still valid before processing
	if not is_instance_valid(self) or not is_inside_tree():
		return
	is_detected = true
	if sprite:
		sprite.modulate.a = 1.0

func _on_disarm_area_body_entered(body: Node) -> void:
	# Show "DISARM" text if player is Dwarf and trap is detected
	if not body.is_in_group("player"):
		return
	
	if not is_detected or is_disarmed:
		return
	
	# CRITICAL: Only show disarm text for LOCAL players who are Dwarves
	# Check if this player is the local player (has authority matching local peer ID)
	var is_local = false
	if body.has_method("is_multiplayer_authority") and body.is_multiplayer_authority():
		# This player is controlled by the local client
		is_local = true
	elif multiplayer.has_multiplayer_peer():
		# Check if this player's authority matches our local peer ID
		var player_authority = body.get_multiplayer_authority()
		var local_peer_id = multiplayer.get_unique_id()
		if player_authority == local_peer_id:
			is_local = true
	
	if is_local:
		# Check if player is Dwarf
		if body.character_stats and body.character_stats.race == "Dwarf":
			_show_disarm_text(body)

func _on_disarm_area_body_exited(body: Node) -> void:
	# Hide disarm text when player leaves area
	if body == disarming_player:
		_cancel_disarm()
	_hide_disarm_text(body)

func _show_disarm_text(_player: Node) -> void:
	# Create "DISARM" label above trap
	if disarm_label:
		return  # Already showing
	
	disarm_label = Label.new()
	disarm_label.text = "DISARM"
	disarm_label.add_theme_font_size_override("font_size", 16)
	disarm_label.add_theme_color_override("font_color", Color.YELLOW)
	disarm_label.add_theme_color_override("font_outline_color", Color.BLACK)
	disarm_label.add_theme_constant_override("outline_size", 2)
	disarm_label.position = Vector2(-25, -30)
	disarm_label.z_index = 100
	add_child(disarm_label)

func _hide_disarm_text(_player: Node) -> void:
	if disarm_label:
		disarm_label.queue_free()
		disarm_label = null

func _update_disarm_ui() -> void:
	# Update disarm progress (could show radial timer here)
	if disarm_label:
		var progress_percent = int((disarm_progress / disarm_duration) * 100)
		disarm_label.text = "DISARM (" + str(progress_percent) + "%)"

func _cancel_disarm() -> void:
	if disarming_player and disarming_player.has_method("set") and "is_disarming" in disarming_player:
		disarming_player.is_disarming = false
	disarming_player = null
	disarm_progress = 0.0
	# Stop disarming sound
	if $SfxDisarming.playing:
		$SfxDisarming.stop()
	if disarm_label:
		disarm_label.text = "DISARM"

func _complete_disarm() -> void:
	# Trap successfully disarmed!
	is_disarmed = true
	if disarming_player and disarming_player.has_method("set") and "is_disarming" in disarming_player:
		disarming_player.is_disarming = false
	disarming_player = null
	disarm_progress = 0.0
	
	# Stop disarming sound
	if $SfxDisarming.playing:
		$SfxDisarming.stop()
	
	# Hide disarm text
	_hide_disarm_text(null)
	
	# Disable activation area
	if activation_area:
		activation_area.monitoring = false
	
	# Show "TRAP DISARMED" in chat
	var chat_ui = get_tree().get_first_node_in_group("chat_ui")
	if chat_ui and chat_ui.has_method("send_system_message"):
		chat_ui.send_system_message("Trap disarmed by Dwarf!")
	
	# Show floating text "TRAP DISARMED"
	_show_floating_text("TRAP DISARMED", Color.YELLOW)
	
	# Change trap visual to show it's disarmed (optional - could fade out or change color)
	sprite.modulate = Color(0.5, 0.5, 0.5, 0.5)
	
	# Grant EXP to all players for disarming trap (only on server)
	# CRITICAL: Only server should grant EXP to avoid duplicates
	if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
		var trap_exp_reward = 8.0  # EXP reward for disarming a trap
		var all_players = get_tree().get_nodes_in_group("player")
		var valid_players = []
		for player in all_players:
			if is_instance_valid(player) and player.character_stats:
				valid_players.append(player)
		
		if valid_players.size() > 0:
			# Split EXP evenly among all players
			var exp_per_player = trap_exp_reward / valid_players.size()
			for player in valid_players:
				player.character_stats.add_xp(exp_per_player)
				LogManager.log("Trap disarmed: granted " + str(exp_per_player) + " EXP to " + str(player.name) + " (shared from " + str(trap_exp_reward) + " total)", LogManager.CATEGORY_ENEMY)
				
				# Sync EXP to client if this player belongs to a client
				var player_peer_id = player.get_multiplayer_authority()
				if player_peer_id != 0 and player_peer_id != multiplayer.get_unique_id() and player.has_method("_sync_stats_update"):
					var coins = player.character_stats.coin if "coin" in player.character_stats else 0
					var xp = player.character_stats.xp if "xp" in player.character_stats else 0.0
					player._sync_stats_update.rpc_id(player_peer_id, player.character_stats.kills, coins, xp)
			
			# Show floating EXP text at trap position and sync to all clients
			# Show locally first
			_show_exp_number(exp_per_player, global_position)
			# Sync to all clients via game_world
			var game_world = get_tree().get_first_node_in_group("game_world")
			if game_world and game_world.has_method("_sync_exp_text_at_position") and multiplayer.has_multiplayer_peer():
				game_world._sync_exp_text_at_position.rpc(exp_per_player, global_position)
	
	# Sync disarm to all clients (including host when joiner disarms)
	if multiplayer.has_multiplayer_peer() and is_inside_tree() and is_instance_valid(self):
		var game_world = get_tree().get_first_node_in_group("game_world")
		if game_world:
			if multiplayer.is_server():
				# Host disarmed: broadcast to clients
				if game_world.has_method("_sync_trap_state_by_name"):
					game_world._sync_trap_state_by_name.rpc(name, true, true)  # detected=true, disarmed=true
			else:
				# Joiner disarmed: request host to apply locally and broadcast to all
				if game_world.has_method("_request_trap_disarm"):
					game_world._request_trap_disarm.rpc_id(1, name)
	
	print("Trap disarmed!")

@rpc("authority", "call_local", "reliable")
func _sync_trap_disarmed() -> void:
	# Client receives trap disarm notification
	# CRITICAL: Validate trap is still valid before processing
	if not is_instance_valid(self) or not is_inside_tree():
		return
	is_disarmed = true
	if sprite:
		sprite.modulate = Color(0.5, 0.5, 0.5, 0.5)
	if activation_area:
		activation_area.monitoring = false

func _show_exp_number(amount: float, exp_pos: Vector2):
	# Show EXP number (green, using dmg_numbers.png font) at position
	var damage_number_scene = preload("res://scenes/damage_number.tscn")
	if not damage_number_scene:
		return
	
	var exp_label = damage_number_scene.instantiate()
	if not exp_label:
		return
	
	# Set text and color for EXP (green)
	exp_label.label = "+" + str(int(amount)) + " EXP"
	exp_label.color = Color(0.4, 1.0, 0.4)  # Bright green
	exp_label.z_index = 5
	
	# Direction is straight up
	exp_label.direction = Vector2(0, -1)
	
	# Position at the specified location
	var game_world = get_tree().get_first_node_in_group("game_world")
	if game_world:
		var entities_node = game_world.get_node_or_null("Entities")
		if entities_node:
			entities_node.add_child(exp_label)
			exp_label.global_position = exp_pos + Vector2(0, -20)  # Slightly above
		else:
			get_tree().current_scene.add_child(exp_label)
			exp_label.global_position = exp_pos + Vector2(0, -20)
	else:
		get_tree().current_scene.add_child(exp_label)
		exp_label.global_position = exp_pos + Vector2(0, -20)

func _show_floating_text(text: String, color: Color) -> void:
	var floating_text_scene = preload("res://scenes/floating_text.tscn")
	if floating_text_scene:
		var floating_text = floating_text_scene.instantiate()
		var parent = get_parent()
		if parent:
			parent.add_child(floating_text)
			floating_text.global_position = Vector2(global_position.x, global_position.y - 20)
			floating_text.setup(text, color, 0.5, 0.5, null, 1, 1, 0)

func _on_activation_area_body_shape_entered(_body_rid: RID, body: Node2D, _body_shape_index: int, _local_shape_index: int) -> void:
	# Trap triggered!
	if not body.is_in_group("player"):
		return
	
	if is_disarmed or is_active:
		return  # Can't trigger if disarmed or already active
	
	if has_cooldown and cooldown_timer > 0:
		return  # Still on cooldown
	
	# Trigger trap
	is_active = true
	$SfxActivate.play()
	animation_player.play("activate")
	
	# Trap is now visible to all players (once triggered)
	if not is_detected:
		is_detected = true
		sprite.modulate.a = 1.0
		# CRITICAL: Validate trap is still valid before sending RPC
		# Use GameWorld RPC to avoid node path issues
		if multiplayer.has_multiplayer_peer() and is_inside_tree() and is_instance_valid(self):
			if multiplayer.is_server():
				# Use GameWorld RPC with trap name instead of path
				var game_world = get_tree().get_first_node_in_group("game_world")
				if game_world and game_world.has_method("_sync_trap_state_by_name"):
					game_world._sync_trap_state_by_name.rpc(name, true, false)  # detected=true, disarmed=false
	
	# Deal damage to player (with luck-based avoidance)
	_deal_trap_damage(body)
	
	# Start cooldown if applicable
	if has_cooldown:
		cooldown_timer = cooldown_time
		await animation_player.animation_finished
		animation_player.play("reset")
		await animation_player.animation_finished
		is_active = false
	else:
		# One-time trap - stays triggered
		pass

func _deal_trap_damage(player: Node) -> void:
	if not player or not player.character_stats:
		return
	
	# Luck-based avoidance check
	var luck_stat = player.character_stats.baseStats.lck + player.character_stats.get_pass("lck")
	var avoid_chance = luck_stat * 0.02  # 2% per luck point (e.g., 10 luck = 20% avoid)
	var avoid_roll = randf()
	
	if avoid_roll < avoid_chance:
		$SfxAvoid.play()
		# Player avoided trap damage!
		print(player.name, " avoided trap damage! (", avoid_chance * 100, "% chance)")
		_show_floating_text("AVOIDED", Color.GREEN)
		return
	
	# Apply trap damage (affected by player's defense)
	var final_damage = player.character_stats.calculate_damage(trap_damage, false, false)
	
	if player.has_method("rpc_take_damage"):
		player.rpc_take_damage(trap_damage, global_position)
	
	print(player.name, " took ", final_damage, " trap damage")
