## BuyZone — Area3D that defines where players can buy weapons/equipment.
##
## A trigger zone that tracks which players are inside it and emits
## enter/exit signals. Buy zones can be filtered by team, so only
## the appropriate team can purchase within a given zone.
##
## In the map scene, place BuyZone nodes (Area3D) near each team's
## spawn area. Set zone_team to restrict which team can use it.
##
## Usage (map tscn):
##   [node name="BuyZone_CT" type="Area3D" parent="."]
##   script = ExtResource("...buy_zone.gd")
##   zone_team = 1          # Team.COUNTER_TERRORIST
##   zone_radius = 10.0
##
##   (Add a CollisionShape3D child with a SphereShape3D for the trigger volume.)
##
## Signals:
##   player_entered_buyzone(player_id) — emitted when a player enters the zone
##   player_exited_buyzone(player_id)  — emitted when a player leaves the zone
##
## Usage (server runtime):
##   var bz: BuyZone = $BuyZone_CT
##   if bz.is_in_buyzone(player_id):
##       show_buy_menu(player_id)

extends Area3D
class_name BuyZone
const _td_bz = preload("res://scripts/teams/team_data.gd")

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

## Emitted when a player enters the buy zone.
signal player_entered_buyzone(player_id: int)

## Emitted when a player exits the buy zone.
signal player_exited_buyzone(player_id: int)


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

## Which team(s) can use this buy zone.
## Team.COUNTER_TERRORIST = 1, Team.TERRORIST = 2, Team.SPECTATOR = 0.
## Use the Team enum: int.COUNTER_TERRORIST, etc.
@export var zone_team: int = _td_bz.Team.COUNTER_TERRORIST

## Radius of the buy zone in Godot units (visual hint only; actual collision
## shape determines the trigger volume).
@export var zone_radius: float = 10.0

## If true, any team can use this zone (overrides zone_team).
@export var allow_all_teams: bool = false


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

## Set of player IDs currently inside this buy zone.
var _players_in_zone: Dictionary = {}  # player_id (int) → true


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

func _ready() -> void:
	# Connect area signals
	body_entered.connect(_on_body_entered)
	body_exited.connect(_on_body_exited)
	area_entered.connect(_on_area_entered)
	area_exited.connect(_on_area_exited)

	# Set collision layer/mask for buy zones (layer 4 by convention)
	collision_layer = 0  # Buy zones don't collide with physics
	collision_mask = 0   # They detect bodies via signals only

	# Ensure we have at least a basic collision shape hint
	_setup_collision_shape()


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

## Check if a specific player is inside this buy zone.
func is_in_buyzone(player_id: int) -> bool:
	return _players_in_zone.has(player_id)


## Get an array of player IDs currently inside this buy zone.
func get_players_in_buyzone() -> Array[int]:
	return _players_in_zone.keys()


## Check if the given team is allowed to use this buy zone.
func is_team_allowed(team: int) -> bool:
	if allow_all_teams:
		return true
	return team == zone_team


## Get the number of players currently in the zone.
func get_player_count() -> int:
	return _players_in_zone.size()


# ---------------------------------------------------------------------------
# Internal: Signal handlers
# ---------------------------------------------------------------------------

func _on_body_entered(body: Node) -> void:
	var player_id: int = _extract_player_id(body)
	if player_id >= 0:
		_players_in_zone[player_id] = true
		player_entered_buyzone.emit(player_id)


func _on_body_exited(body: Node) -> void:
	var player_id: int = _extract_player_id(body)
	if player_id >= 0 and _players_in_zone.erase(player_id):
		player_exited_buyzone.emit(player_id)


func _on_area_entered(_area: Area3D) -> void:
	# Areas can also trigger if the player is an Area3D (e.g. a trigger zone
	# on the player). For now, body detection is the primary path.
	pass


func _on_area_exited(_area: Area3D) -> void:
	pass


# ---------------------------------------------------------------------------
# Internal: helpers
# ---------------------------------------------------------------------------

## Try to extract a multiplayer peer ID from a body node.
## The body is expected to be a Player node or have a "get_peer_id" method,
## or the body's owner is a Player node with multiplayer authority info.
func _extract_player_id(body: Node) -> int:
	# If the body has a peer_id property or method
	if body.has_method(&"get_peer_id"):
		return body.get_peer_id()

	# If the body is a Player node with multiplayer authority
	if body.has_method(&"get_multiplayer_authority"):
		return body.get_multiplayer_authority()

	# Check for a direct peer_id variable
	if "peer_id" in body and body.get("peer_id") is int:
		return body.get("peer_id")

	# Try the body's owner
	var owner_node: Node = body.owner if body.owner else body.get_parent()
	if owner_node and owner_node != body:
		return _extract_player_id(owner_node)

	return -1


## Ensure there's a collision shape for the trigger volume.
## If no CollisionShape3D child exists, create a SphereShape3D one.
func _setup_collision_shape() -> void:
	# Check if we already have a CollisionShape3D
	for child in get_children():
		if child is CollisionShape3D:
			return  # Already have one

	# Create a default sphere shape
	var shape := SphereShape3D.new()
	shape.radius = zone_radius

	var col_shape := CollisionShape3D.new()
	col_shape.shape = shape
	add_child(col_shape)
	col_shape.owner = get_tree().edited_scene_root if Engine.is_editor_hint() else owner
