## BombCarrier — Client-side bomb status UI and prompts.
##
## Attached to the local T player who carries the bomb.
## Provides visual feedback for:
##   - Bomb status (planted/defusing/timer)
##   - Direction indicator to planted bomb
##   - "Hold E to defuse" prompt (CT, near bomb)
##   - "Hold E to plant" prompt (T, in bombsite, LIVE phase)
##
## This script is a Node that should be added as a child of the local
## player's character node (FPSCharacterController) on the client side.
## It communicates with the server BombObjective via RPC or state sync.

extends Node
class_name BombCarrier

# ---------------------------------------------------------------------------
# Signals
# ---------------------------------------------------------------------------

## Emitted when the nearby interaction prompt changes.
signal prompt_changed(prompt_text: String, visible: bool)

## Emitted when bomb status UI should update.
signal bomb_status_updated(planted: bool, timer: float, defusing: bool, defuse_progress: float)

## Emitted when the bomb direction indicator should update.
signal bomb_direction_updated(bearing_degrees: float, distance: float)

# ---------------------------------------------------------------------------
# Exports
# ---------------------------------------------------------------------------

## The "Use/Interact" action name (matched in Input Map).
@export var interact_action: String = "interact"

## Interaction distance for planting (in Godot units).
@export var plant_distance: float = 2.0

## Interaction distance for defusing (in Godot units).
@export var defuse_distance: float = 3.0

# ---------------------------------------------------------------------------
# State
# ---------------------------------------------------------------------------

## Whether this player has the bomb (T-side bomb carrier).
var has_bomb: bool = false

## Whether the bomb is currently planted.
var _bomb_planted: bool = false

## Position of the planted bomb (server-authoritative).
var _bomb_position: Vector3 = Vector3.ZERO

## Remaining bomb timer (received from server).
var _bomb_timer: float = 0.0

## Whether the local player is currently defusing.
var _is_defusing: bool = false

## Current defuse progress (0.0 to 1.0).
var _defuse_progress: float = 0.0

## Current prompt text to show.
var _current_prompt: String = ""

## Whether the prompt is currently visible.
var _prompt_visible: bool = false

## Whether we are inside a bombsite.
var _in_bombsite: bool = false

## Whether we are near the planted bomb.
var _near_planted_bomb: bool = false

## Whether the interact key is being held.
var _interact_held: bool = false

## Hold-down timer for plant/defuse (E key held long enough).
var _interact_hold_time: float = 0.0

## Time required to hold E to start action (for UX consistency).
var _interact_hold_duration: float = 0.0  # Immediate on press

## Reference to the local player's parent/character controller.
var _character_controller: Node = null

## Reference to the bomb objective on the server (for RPC calls).
## Set by the game manager after instantiation.
var bomb_objective_path: NodePath = NodePath()

# ---------------------------------------------------------------------------
# Lifecycle
# ---------------------------------------------------------------------------

func _ready() -> void:
	_character_controller = get_parent()
	if _character_controller == null:
		push_warning("[BombCarrier] No parent node — prompts will not work")

	# Try to set up the interact action if it doesn't exist
	if not InputMap.has_action(interact_action):
		var event := InputEventKey.new()
		event.keycode = KEY_E
		InputMap.add_action(interact_action)
		InputMap.action_add_event(interact_action, event)

	print("[BombCarrier] Initialised — has_bomb=%s" % has_bomb)

func _process(delta: float) -> void:
	_update_interaction(delta)
	_emit_status_updates()

# ---------------------------------------------------------------------------
# Public API — Server state sync
# ---------------------------------------------------------------------------

## Called by the game manager or network sync to update bomb state.
func sync_bomb_state(planted: bool, position: Vector3, timer: float,
		defusing: bool, defuse_progress: float) -> void:
	_bomb_planted = planted
	_bomb_position = position
	_bomb_timer = timer
	_is_defusing = defusing
	_defuse_progress = defuse_progress

## Set whether this player has the bomb (assigned by server).
func set_has_bomb(value: bool) -> void:
	has_bomb = value
	print("[BombCarrier] Has bomb: %s" % value)

## Set whether the local player is inside a bombsite Area3D.
## Called by the bombsite Area3D signal handlers on the client.
func set_in_bombsite(value: bool) -> void:
	_in_bombsite = value

## Set whether the local player is near the planted bomb.
## Called by the bomb area proximity check.
func set_near_planted_bomb(value: bool) -> void:
	_near_planted_bomb = value

# ---------------------------------------------------------------------------
# Public API — RPC calls to server
# ---------------------------------------------------------------------------

## Request to plant the bomb at the current position.
## Called when the T player presses E inside a bombsite with the bomb.
func request_plant() -> void:
	if not has_bomb or not _in_bombsite:
		return
	if _bomb_planted:
		return

	# Send plant request to server
	# In real implementation, this would be an RPC call:
	# _send_plant_request.rpc_id(1, _character_controller.global_position)
	_send_plant_request(_character_controller.global_position)

## Request to start defusing the bomb.
## Called when the CT player presses E near the planted bomb.
func request_defuse() -> void:
	if not _near_planted_bomb or not _bomb_planted:
		return
	if _is_defusing:
		return

	# Send defuse request to server
	# _send_defuse_request.rpc_id(1)
	_send_defuse_request()

## Request to cancel defuse (when player moves or is interrupted).
func request_cancel_defuse() -> void:
	if not _is_defusing:
		return
	_send_cancel_defuse()

# ---------------------------------------------------------------------------
# RPC stubs (to be replaced with actual RPC calls)
# ---------------------------------------------------------------------------

func _send_plant_request(position: Vector3) -> void:
	# Placeholder: in real implementation, send RPC to server
	print("[BombCarrier] Plant request at (%.1f, %.1f, %.1f)" %
		[position.x, position.y, position.z])

func _send_defuse_request() -> void:
	# Placeholder: in real implementation, send RPC to server
	print("[BombCarrier] Defuse request")

func _send_cancel_defuse() -> void:
	# Placeholder: in real implementation, send RPC to server
	print("[BombCarrier] Cancel defuse request")

# ---------------------------------------------------------------------------
# Internal — Interaction logic
# ---------------------------------------------------------------------------

func _update_interaction(delta: float) -> void:
	var interact_pressed: bool = Input.is_action_pressed(interact_action)
	var prompt_text: String = ""
	var show_prompt: bool = false

	# Determine what prompt to show
	if _bomb_planted and _near_planted_bomb and not _is_defusing:
		# CT player near planted bomb — defuse prompt
		var team: int = _get_local_player_team()
		if team == TeamData.Team.COUNTER_TERRORIST:
			prompt_text = "Hold E to defuse"
			show_prompt = true

	elif has_bomb and _in_bombsite and not _bomb_planted:
		# T player with bomb inside bombsite — plant prompt
		if _is_round_live():
			prompt_text = "Hold E to plant"
			show_prompt = true

	elif _bomb_planted and _is_defusing:
		# Currently defusing — show progress
		var progress_pct: int = int(_defuse_progress * 100.0)
		prompt_text = "Defusing... %d%%" % progress_pct
		show_prompt = true

	# Handle interact hold
	if interact_pressed and show_prompt:
		if not _interact_held:
			_interact_held = true
			_interact_hold_time = 0.0

		_interact_hold_time += delta

		# Check if we should trigger the action
		if _interact_hold_time >= _interact_hold_duration:
			_interact_hold_time = 0.0
			if prompt_text.contains("defuse"):
				request_defuse()
			elif prompt_text.contains("plant"):
				request_plant()
	else:
		# Cancel any pending interaction
		if _interact_held:
			_interact_held = false
			_interact_hold_time = 0.0
			# If we were defusing and released, cancel
			if _is_defusing:
				request_cancel_defuse()

	# Update prompt if changed
	if prompt_text != _current_prompt or show_prompt != _prompt_visible:
		_current_prompt = prompt_text
		_prompt_visible = show_prompt
		prompt_changed.emit(prompt_text, show_prompt)

func _emit_status_updates() -> void:
	# Emit bomb status updates (rate-limited is handled by engine)
	bomb_status_updated.emit(_bomb_planted, _bomb_timer, _is_defusing, _defuse_progress)

	# Emit direction indicator if bomb is planted
	if _bomb_planted and _character_controller != null:
		var player_pos: Vector3 = _character_controller.global_position
		var direction: Vector3 = _bomb_position - player_pos
		var distance: float = direction.length()
		if distance > 0.01:
			var bearing: float = atan2(direction.x, -direction.z)  # Relative to forward (Z)
			bomb_direction_updated.emit(rad_to_deg(bearing), distance)
	else:
		bomb_direction_updated.emit(0.0, 0.0)

# ---------------------------------------------------------------------------
# Internal — Helpers
# ---------------------------------------------------------------------------

## Get the local player's team (from TeamManager singleton if available).
func _get_local_player_team() -> int:
	var team_manager: TeamManager = get_node_or_null("/root/TeamManager")
	if team_manager:
		var peer_id: int = multiplayer.get_unique_id() if multiplayer.has_method(&\"get_unique_id\") else -1
		if peer_id >= 0:
			return team_manager.get_player_team(peer_id)
	return -1

## Check if the current round is in LIVE phase.
func _is_round_live() -> bool:
	var round_manager: RoundManager = get_node_or_null("/root/RoundManager")
	if round_manager:
		return round_manager.get_phase() == RoundManager.RoundPhase.LIVE
	return false

## Get the multiplayer API (stub for compatibility).
func get_multiplayer() -> MultiplayerAPI:
	if multiplayer and multiplayer.has_method(&\"get_unique_id\"):
		return multiplayer
	return null
